Statistics
| Revision:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
526
                return pScreen;
527
        }
528

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

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

    
568
                return toMapPoint(pScreen);
569
        }
570

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

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

    
612
                return dist;
613
        }
614

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

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

    
642
                return pWorld;
643
        }
644

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1048
                double escalaX;
1049
                double escalaY;
1050

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

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

    
1059
                adjustedExtent = new Rectangle2D.Double();
1060

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

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

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

    
1095
                trans.concatenate(translacion);
1096

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1594
                state.set("adjustedExtent", adjustedExtent);
1595
                state.set("backColor", backColor);
1596
                state.set("clip", cliprect);
1597
                state.set("dist1pixel", dist1pixel);
1598
                state.set("dist3pixel", dist3pixel);
1599
                state.set("distanceUnits", distanceUnits);
1600
                state.set("distanceArea", distanceArea);
1601
                
1602
                state.set("extent", extent);
1603
                state.set("extents", extents);
1604
                
1605
                state.set("mapUnits", mapUnits);
1606
                state.set("offsetX", offset);
1607

    
1608
                state.set("proj", proj);
1609

    
1610
                state.set("imageSize", imageSize);
1611
        }
1612
        
1613
        
1614
        
1615
        public static void registerPersistent() {
1616
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
1617
                DynStruct definition = manager.addDefinition(
1618
                                ViewPort.class,
1619
                                "ViewPort",
1620
                                "ViewPort Persistence definition",
1621
                                null, 
1622
                                null
1623
                );
1624
                definition.addDynFieldString("comment");
1625
                definition.addDynFieldInt("code").setMandatory(true);
1626
                definition.addDynFieldString("creationDate").setMandatory(true);
1627
                definition.addDynFieldString("name").setMandatory(true);
1628
                definition.addDynFieldString("owner");
1629
                definition.addDynFieldBoolean("locked");
1630
                
1631
                
1632
        definition.addDynFieldObject("adjustedExtent")
1633
                .setClassOfValue(Rectangle2D.class)
1634
                .setMandatory(true);
1635
        
1636
        definition.addDynFieldString("backColor")
1637
                .setClassOfValue(Color.class)
1638
                .setMandatory(true);
1639
        
1640
        definition.addDynFieldObject("clip")
1641
                .setClassOfValue(Rectangle2D.class)
1642
                .setMandatory(true);
1643
        
1644
        definition.addDynFieldDouble("dist1pixel")
1645
                .setMandatory(true);
1646

    
1647
        definition.addDynFieldDouble("dist3pixel")
1648
                .setMandatory(true);
1649

    
1650
        definition.addDynFieldInt("distanceUnits")
1651
                .setMandatory(true);
1652
        
1653
        definition.addDynFieldInt("distanceArea")
1654
                .setMandatory(false);
1655
        
1656
        definition.addDynFieldObject("extent")
1657
                .setClassOfValue(Rectangle2D.class)
1658
                .setMandatory(true);
1659
        
1660
        definition.addDynFieldObject("extents")
1661
                .setClassOfValue(ExtentHistory.class)
1662
                .setMandatory(true);
1663
        
1664
        definition.addDynFieldInt("mapUnits")
1665
                .setMandatory(true);
1666
        
1667
        definition.addDynFieldDouble("offset")
1668
                .setClassOfValue(Point2D.class)
1669
                .setMandatory(true);
1670

    
1671
        definition.addDynFieldObject("proj")
1672
                .setClassOfValue(IProjection.class)
1673
                .setMandatory(true);
1674
        
1675
        definition.addDynFieldInt("imageSize")
1676
                .setClassOfValue(Dimension.class)
1677
                .setMandatory(true);
1678
        }        
1679

    
1680
        public void loadFromState(PersistentState state) throws PersistenceException {
1681
                
1682
                adjustedExtent = (Rectangle2D) state.get("adjustedExtent");
1683
                backColor = (Color) state.get("backColor");
1684
                cliprect = (Rectangle2D) state.get("clip");
1685
                dist1pixel = state.getDouble("dist1pixel");
1686
                dist3pixel = state.getDouble("dist3pixel");
1687
                distanceUnits = state.getInt("distanceUnits");
1688
                extents = (ExtentHistory) state.get("extents");
1689
                extent = (Rectangle2D) state.get("extent");
1690
                mapUnits = state.getInt("mapUnits");
1691
                offset =  (Point2D) state.get("offset");
1692
                proj = (IProjection) state.get("proj");
1693
                imageSize =  (Dimension) state.get("imageSize");
1694

    
1695
                try {
1696
                        distanceArea = state.getInt("distanceArea");
1697
                } catch (Exception pvnf) {
1698
                        // not mandatory , use dist units
1699
                        distanceArea = distanceUnits;
1700
                }        
1701
                
1702
                refreshExtent();
1703
        }
1704

    
1705
        /**
1706
         * <p>
1707
         * Fast clone implementation: creates and returns a clone of this view port
1708
         * using XML entities.
1709
         * </p>
1710
         * <p>
1711
         * Isn't a <i>deepclone</i> to avoid unnecessary memory consumption.
1712
         * </p>
1713
         * 
1714
         * @return the new view port
1715
         * 
1716
         * @see #createFromXML(XMLEntity)
1717
         */
1718
        public ViewPort cloneViewPort() {
1719
            // TODO: make this class implement clonable.
1720
            //       see http://www.gvsig.org/web/projects/gvsig-desktop/docs/devel/org.gvsig.tools/2.1.0/org-gvsig-tools.lang
1721
                logger.warn("TODO: make this class implement clonable.");
1722
                return null; // createFromXML(getXMLEntity());
1723
        }
1724

    
1725
        /**
1726
         * <p>
1727
         * Returns a <code>String</code> representation of the main values of this
1728
         * view port: <code>{@linkplain #extent}</code>,
1729
         * <code>{@linkplain #adjustedExtent}</code>,
1730
         * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>,
1731
         * and <code>{@linkplain #trans}</code>.
1732
         * </p>
1733
         * 
1734
         * @return a <code>string</code> representation of the main values of this
1735
         *         view port
1736
         */
1737
        public String toString() {
1738

    
1739
                String str;
1740
                str = "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1741
                                + adjustedExtent + "\nimageSize=" + imageSize + "\nescale="
1742
                                + scale + "\ntrans=" + trans;
1743

    
1744
                return str;
1745
        }
1746

    
1747
        /**
1748
         * <p>
1749
         * Sets the position and size of the clipping rectangle.
1750
         * </p>
1751
         * 
1752
         * @param rectView
1753
         *            the clipping rectangle to set
1754
         */
1755
        public void setClipRect(Rectangle2D rectView) {
1756
                this.updateDrawVersion();
1757
                cliprect = rectView;
1758
        }
1759

    
1760
        /**
1761
         * <p>
1762
         * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
1763
         * <i>map coordinates</i> to <i>screen coordinates</i> (pixels) using an
1764
         * <i>inverse transform</i> with the transformation affine information in
1765
         * the {@link #trans #trans} attribute.
1766
         * </p>
1767
         * 
1768
         * @param r
1769
         *            the 2D rectangle in <i>map coordinates</i>
1770
         * @return 2D rectangle equivalent in <i>screen coordinates</i> (pixels)
1771
         * 
1772
         * @see #toMapRectangle(Rectangle2D)
1773
         * @see #fromMapDistance(double)
1774
         * @see #fromMapPoint(Point2D)
1775
         */
1776
        public Rectangle2D fromMapRectangle(Rectangle2D r) {
1777
                Rectangle2D rect = new Rectangle2D.Double();
1778
                Point2D p1 = fromMapPoint((int) r.getX(), (int) r.getY());
1779
                Point2D p2 = fromMapPoint((int) r.getMaxX(), (int) r.getMaxY());
1780
                rect.setFrameFromDiagonal(p1, p2);
1781
                return rect;
1782
        }
1783

    
1784
        /**
1785
         * <p>
1786
         * Recalculates the current <code>{@linkplain #extent}</code> using an
1787
         * scale. It's necessary execute {@linkplain #refreshExtent()} after.
1788
         * </p>
1789
         * 
1790
         * @param s
1791
         *            the scale to set
1792
         * 
1793
         * @deprecated since 07/09/07, use
1794
         *             {@linkplain MapContext#setScaleView(long)}
1795
         */
1796
        public void setScale(long s) {
1797
                double x = extent.getX();
1798
                double y = extent.getY();
1799
                double escalaX = imageSize.width / extent.getWidth();
1800
//                double w = imageSize.width / s;
1801
//                double h = imageSize.height / s;
1802
                double difw = escalaX / s;
1803

    
1804
                double x1 = (-x * difw) - x + extent.getWidth() / 2;
1805
                double y1 = (-y * difw) - y + extent.getHeight() / 2;
1806
                double w1 = extent.getWidth() * difw;
1807
                double h1 = extent.getHeight() * difw;
1808
                extent.setRect(-x1, -y1, w1, h1);
1809
        }
1810

    
1811
        public long getDrawVersion() {
1812
                return this.drawVersion;
1813
        }
1814

    
1815
        protected void updateDrawVersion() {
1816
                this.drawVersion++;
1817
        }
1818
        
1819
        
1820
        
1821
        
1822
        
1823
    /**
1824
     * Obtiene la representaci?n de un color como String
1825
     *
1826
     * @param c Color
1827
     *
1828
     * @return String
1829
     */
1830
    public static String color2String(Color c) {
1831
            if (c == null) return null;
1832
        return c.getRed() + "," + c.getGreen() + "," + c.getBlue() + "," +
1833
        c.getAlpha();
1834
    }
1835

    
1836
    /**
1837
     * Obtiene el color de un string generado con color2String 
1838
     *
1839
     * @param stringColor string 
1840
     *
1841
     * @return Color
1842
     */
1843
    public static Color string2Color(String stringColor) {
1844
            if (stringColor == null || stringColor.equals("null")) return null;
1845
        String[] ints = new String[4];
1846
        
1847
        ints = CompatLocator.getStringUtils().split(stringColor, ",");
1848

    
1849
        int[] ret = new int[4];
1850

    
1851
        for (int i = 0; i < ret.length; i++) {
1852
            ret[i] = new Integer(ints[i]).intValue();
1853
        }
1854

    
1855
        return new Color(ret[0], ret[1], ret[2], ret[3]);
1856

    
1857
        /*
1858
           long color = new Long(stringColor).longValue();
1859
           long alpha = color / 16777216;
1860
           color = color % 16777216;
1861
        
1862
           long red = color / 65536;
1863
           color = color % 65536;
1864
           long green = color / 256;
1865
           color = color % 256;
1866
           long blue = color;
1867
           return new Color(red, green, blue, alpha);*/
1868
    }
1869

    
1870

    
1871

    
1872

    
1873
}