Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / ViewPort.java @ 36203

History | View | Annotate | Download (45.9 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 com.iver.cit.gvsig.fmap;
42

    
43
import java.awt.Color;
44
import java.awt.Dimension;
45
import java.awt.Point;
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.gt2.CSUTM;
55

    
56
import com.iver.cit.gvsig.fmap.crs.CRSFactory;
57
import com.iver.utiles.StringUtilities;
58
import com.iver.utiles.XMLEntity;
59

    
60

    
61
/**
62
 * <p><code>ViewPort</code> class represents the logic needed to transform a rectangular area of a map
63
 *  to the available area in screen to display it.</p>
64
 *
65
 * <p>Includes an affine transformation, between the rectangular area selected of the external map, in its own
66
 *  <i>map coordinates</i>, to the rectangular area available of a view in <i>screen coordinates</i>.</p>
67
 *
68
 * <p>Elements:
69
 * <ul>
70
 * <li><i>extent</i>: the area selected of the map, in <i>map coordinates</i>.
71
 * <li><i>imageSize</i>: width and height in pixels (<i>screen coordinates</i>) of the area available
72
 *  in screen to display the area selected of the map.
73
 * <li><i>adjustedExtent</i>: the area selected must be an scale of <i>imageSize</i>.<br>This implies adapt the
74
 *  extent, preserving and centering it, and adding around the needed area to fill all the image size. That
75
 *  added area will be extracted from the original map, wherever exists, and filled with the background color
76
 *  wherever not.
77
 * <li><i>scale</i>: the scale between the adjusted extent and the image size.
78
 * <li><i>backColor</i>: the default background color in the view, if there is no map.
79
 * <li><i>trans</i>: the affine transformation.
80
 * <li><i>proj</i>: map projection used in this view.
81
 * <li><i>distanceUnits</i>: distance measurement units, of data in screen.
82
 * <li><i>mapUnits</i>: measurement units, of data in map.
83
 * <li><i>extents</i>: an {@link ExtentHistory ExtentHistory} with the last previous extents.
84
 * <li><i>offset</i>: position in pixels of the available rectangular area, where start drawing the map.
85
 * <li><i>dist1pixel</i>: the distance in <i>world coordinates</i> equivalent to 1 pixel in the view with the
86
 *  current extent.
87
 * <li><i>dist3pixel</i>: the distance in <i>world coordinates</i> equivalent to 3 pixels in the view with the
88
 *  current extent.
89
 * <li><i>listeners</i>: list with the {@link ViewPortListener ViewPortListener} registered.
90
 * </ul>
91
 * </p>
92
 *
93
 * @author Vicente Caballero Navarro
94
 */
95
public class ViewPort {
96
//        /**
97
//         * <p>Metric unit or length equal to 1000 meters.</p>
98
//         */
99
//        public static int KILOMETROS = 0;
100
//
101
//        /**
102
//         * <p>The base unit of length in the International System of Units that is equal to the distance
103
//         *  traveled by light in a vacuum in {frac;1;299,792,458} second or to about 39.37 inches.</p>
104
//         */
105
//        public static int METROS = 1;
106
//
107
//        /**
108
//         * <p>Metric unit or length equal to 0'01 meters.</p>
109
//         */
110
//        public static int CENTIMETRO = 2;
111
//
112
//        /**
113
//         * <p>Metric unit or length equal to 0'001 meters.</p>
114
//         */
115
//        public static int MILIMETRO = 3;
116
//
117
//        /**
118
//         * <p>The international statute mile by international agreement. It is defined to be precisely
119
//         *  1,760 international yards (by definition, 0.9144 m each) and is therefore exactly 1,609.344
120
//         *  metres (1.609344 km).</p>
121
//         */
122
//        public static int MILLAS = 4;
123
//
124
//        /**
125
//         * <p>Unit of length equal in the United States to 0.9144 meter.</p>
126
//         */
127
//        public static int YARDAS = 5;
128
//
129
//        /**
130
//         * <p>Any of various units of length based on the length of the human foot; especially :
131
//         *  a unit equal to 1/3 yard and comprising 12 inches.</p>
132
//         */
133
//        public static int PIES = 6;
134
//
135
//        /**
136
//         * <p>Unit of length equal to 1/36 yard.</p>
137
//         */
138
//        public static int PULGADAS = 7;
139
//
140
//        /**
141
//         * <p>Grades according the current projection.</p>
142
//         */
143
//        public static int GRADOS = 8;
144

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

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

    
176
        /**
177
         * <p>Location and dimensions of the extent adjusted to the image size.</p>
178
         *
179
         * @see #getAdjustedExtent()
180
         */
181
        protected Rectangle2D adjustedExtent;
182

    
183
        /**
184
         * Draw version of the context. It's used for know when de componend has
185
         * changed any visualization property
186
         *
187
         *  @see getDrawVersion
188
         *  @see updateDrawVersion
189
         */
190
        private long drawVersion= 0L;
191

    
192
        /**
193
         * <p>History with the last extents of the view.</p>
194
         *
195
         * @see #setPreviousExtent()
196
         * @see #getExtents()
197
         */
198
        protected ExtentHistory extents = new ExtentHistory();
199

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

    
220
        /**
221
         * <p>the affine transformation between the {@link #extent extent} in <i>map 2D coordinates</i> to
222
         *  the image area in the screen, in <i>screen 2D coordinates</i> (pixels).</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>Measurement unit used for measuring distances and displaying information.</p>
234
         *
235
         * @see #getDistanceUnits()
236
         * @see #setDistanceUnits(int)
237
         */
238
        private int distanceUnits = 1;
239
        /**
240
         * <p>Measurement unit used for measuring areas and displaying information.</p>
241
         *
242
         * @see #getDistanceArea()
243
         * @see #setDistanceArea(int)
244
         */
245
        private int distanceArea = 1;
246
        /**
247
         * <p>Measurement unit used by this view port for the map.</p>
248
         *
249
         * @see #getMapUnits()
250
         * @see #setMapUnits(int)
251
         */
252
        private int mapUnits = 1;
253

    
254
        /**
255
         * <p>Array with the {@link ViewPortListener ViewPortListener}s registered to this view port.</p>
256
         *
257
         * @see #addViewPortListener(ViewPortListener)
258
         * @see #removeViewPortListener(ViewPortListener)
259
         */
260
        private ArrayList listeners = new ArrayList();
261

    
262
        /**
263
         * <p>The offset is the position where start drawing the map.</p>
264
         * <p>The offset of a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s <i>View</i> is
265
         * always (0, 0) because the drawing area fits with the full window area. But in
266
         * a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s <i>Layout</i> it's up to the place where
267
         * the <code>FFrameView</code> is located.</p>
268
         *
269
         * @see #getOffset()
270
         * @see #setOffset(Point2D)
271
         */
272
        private Point2D offset = new Point2D.Double(0, 0);
273

    
274
        /**
275
         * <p>Clipping area.</p>
276
         */
277
        private Rectangle2D clip;
278

    
279
        /**
280
         * <p>Background color of this view.</p>
281
         *
282
         * @see #getBackColor()
283
         * @see #setBackColor(Color)
284
         */
285
        private Color backColor = null; //Color.WHITE;
286

    
287
        /**
288
         * <p>Information about the map projection used in this view.</p>
289
         *
290
         * @see #getProjection()
291
         * @see #setProjection(IProjection)
292
         */
293
        private IProjection proj;
294

    
295
        /**
296
         * <p>Represents the distance in <i>world coordinates</i> equivalent to 1 pixel in the view with the current extent.</p>
297
         *
298
         * @see #getDist1pixel()
299
         * @see #setDist1pixel(double)
300
         */
301
        private double dist1pixel;
302

    
303
        /**
304
         * <p>Represents the distance in <i>world coordinates</i> equivalent to 3 pixels in the view with the current extent.</p>
305
         *
306
         * @see #getDist3pixel()
307
         * @see #setDist3pixel(double)
308
         */
309
        private double dist3pixel;
310

    
311
        /**
312
         * <p>Ratio between the size of <code>imageSize</code> and <code>extent</code>:<br> <i><pre>min{(imageSize.getHeight()/extent.getHeight(), imageSize.getWidth()/extent.getWidth())}</pre></i></p>
313
         */
314
        private double scale;
315

    
316
        /**
317
         * <p>Clipping area.</p>
318
         *
319
         * @see #setClipRect(Rectangle2D)
320
         */
321
        private Rectangle2D cliprect;
322

    
323
        /**
324
         * <p>Enables or disables the <i>"adjustable extent"</i> mode.</p>
325
         *
326
         * <p>
327
         * When calculates the affine transform, if
328
         * <ul>
329
         * <li><i>enabled</i>: the new <code>adjustedExtent</code> will have the (X, Y) coordinates of the <code>extent</code> and
330
         *  an area that will be an scale of the image size. That area will have different
331
         *  height or width (not both) of the extent according the least ratio (height or width) in <pre>image.size/extent.size"</pre>.
332
         * <li><i>disabled</i>: the new <code>adjustedExtent</code> will be like <code>extent</code>.
333
         * </ul>
334
         * </p>
335
         *
336
         * @see #setAdjustable(boolean)
337
         */
338
        private boolean adjustableExtent=true;
339

    
340
        /**
341
         * <p>Creates a new view port with the information of the projection in <code>proj</code> argument, and
342
         *  default configuration:</p>
343
         * <p>
344
         * <ul>
345
         *  <li><i><code>distanceUnits</code></i> = meters
346
         *  <li><i><code>mapUnits</code></i> = meters
347
         *  <li><i><code>backColor</code></i> = <i>undefined</i>
348
         *  <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
349
         * </ul>
350
         * </p>
351
         *
352
         * @param proj information of the projection for this view port
353
         */
354
        public ViewPort(IProjection proj) {
355
                // Por defecto
356
                this.proj = proj;
357
        }
358

    
359
        /**
360
         * <p>Changes the status of the <i>"adjustable extent"</i> option to enabled or disabled.</p>
361
         *
362
         * <p>If view port isn't adjustable, won't bear in mind the aspect ratio of the available rectangular area to
363
         *  calculate the affine transform from the original map in real coordinates. (Won't scale the image to adapt
364
         *  it to the available rectangular area).</p>
365
         *
366
         * @param boolean the boolean to be set
367
         */
368
        public void setAdjustable(boolean adjustable) {
369
                if (adjustable == adjustableExtent){
370
                        return;
371
                }
372
                adjustableExtent = adjustable;
373
                this.updateDrawVersion();
374
        }
375

    
376
        /**
377
         * <p>Appends the specified {@link ViewPortListener ViewPortListener} listener if weren't.</p>
378
         *
379
         * @param arg0 the listener to add
380
         *
381
         * @return <code>true</code> if has been added successfully
382
         *
383
         * @see #removeViewPortListener(ViewPortListener)
384
         */
385
        public boolean addViewPortListener(ViewPortListener arg0) {
386
                if (!listeners.contains(arg0))
387
                        return listeners.add(arg0);
388
                return false;
389
        }
390

    
391
        /**
392
          * <p>Removes the specified {@link ViewPortListener ViewPortListener} listener, if existed.</p>
393
         *
394
         * @param arg0 the listener to remove
395
         *
396
         * @return <code>true</code> if the contained the specified listener.
397
         *
398
         * @see #addViewPortListener(ViewPortListener)
399
         */
400
        public boolean removeViewPortListener(ViewPortListener arg0) {
401
                return listeners.remove(arg0);
402
        }
403

    
404
        /**
405
         * <p>Converts and returns the distance <code>d</code>, that is in <i>map
406
         *  coordinates</i> to <i>screen coordinates</i> using a <i>delta transform</i> with
407
         *  the transformation affine information in the {@link #trans #trans} attribute.</p>
408
         *
409
         * @param d distance in <i>map coordinates</i>
410
         *
411
         * @return distance equivalent in <i>screen coordinates</i>
412
         *
413
         * @see #toMapDistance(int)
414
         * @see AffineTransform#deltaTransform(Point2D, Point2D)S
415
         */
416
        public int fromMapDistance(double d) {
417
                Point2D.Double pWorld = new Point2D.Double(1, 1);
418
                Point2D.Double pScreen = new Point2D.Double();
419

    
420
                try {
421
                        trans.deltaTransform(pWorld, pScreen);
422
                } catch (Exception e) {
423
                        System.err.print(e.getMessage());
424
                }
425

    
426
                return (int) Math.round(d * pScreen.x);
427
        }
428

    
429
        /**
430
         * <p>Converts and returns the 2D point <code>(x,y)</code>, that is in <i>map
431
         *  coordinates</i> to <i>screen coordinates</i> (pixels) using
432
         *  the affine transformation in the {@link #trans #trans} attribute.</p>
433
         *
434
         * @param x the <code>x</code> <i>map coordinate</i> of a 2D point
435
         * @param y the <code>y</code> <i>map coordinate</i> of a 2D point
436
         *
437
         * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
438
         *
439
         * @see #fromMapPoint(Point2D)
440
         * @see AffineTransform#transform(Point2D, Point2D)
441
         */
442
        public Point2D fromMapPoint(double x, double y) {
443
                Point2D.Double pWorld = new Point2D.Double(x, y);
444
                Point2D.Double pScreen = new Point2D.Double();
445

    
446
                try {
447
                        trans.transform(pWorld, pScreen);
448
                } catch (Exception e) {
449
                        System.err.print(e.getMessage());
450
                }
451

    
452
                return pScreen;
453
        }
454

    
455
        /**
456
         * <p>Converts and returns the 2D point argument, that is in <i>map
457
         *  coordinates</i> to <i>screen coordinates</i> (pixels) using
458
         *  the affine transformation in the {@link #trans #trans} attribute.</p>
459
         *
460
         * @param point the 2D point in <i>map coordinates</i>
461
         *
462
         * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
463
         *
464
         * @see #toMapPoint(Point2D)
465
         * @see #fromMapPoint(double, double)
466
         */
467
        public Point2D fromMapPoint(Point2D point) {
468
                return fromMapPoint(point.getX(), point.getY());
469
        }
470

    
471
        /**
472
         * <p>Converts and returns the 2D point <code>(x,y)</code>, that is in <i>screen coordinates</i>
473
         *  (pixels) to <i>map coordinates</i> using
474
         *  the affine transformation in the {@link #trans #trans} attribute.</p>
475
         *
476
         * @param x the <code>x</code> <i>screen coordinate</i> of a 2D point
477
         * @param y the <code>y</code> <i>screen coordinate</i> of a 2D point
478
         *
479
         * @return 2D point equivalent in <i>map coordinates</i>
480
         *
481
         * @see #toMapPoint(Point2D)
482
         * @see #fromMapPoint(double, double)
483
         */
484
        public Point2D toMapPoint(int x, int y) {
485
                Point pScreen = new Point(x, y);
486

    
487
                return toMapPoint(pScreen);
488
        }
489

    
490
        /**
491
         * <p>Converts and returns the {@link Rectangle2D Rectangle2D}, that is in <i>screen
492
         *  coordinates</i> (pixels) to <i>map coordinates</i> using {@linkplain #toMapDistance(int)},
493
         *  and {@linkplain #toMapPoint(int, int)}.</p>
494
         *
495
         * @param r the 2D rectangle in <i>screen coordinates</i> (pixels)
496
         * @return 2D rectangle equivalent in <i>map coordinates</i>
497
         *
498
         * @see #fromMapRectangle(Rectangle2D)
499
         * @see #toMapDistance(int)
500
         * @see #toMapPoint(int, int)
501
         */
502
        public Rectangle2D toMapRectangle(Rectangle2D r){
503
                Rectangle2D rect=new Rectangle2D.Double();
504
                Point2D p1=toMapPoint((int)r.getX(),(int)r.getY());
505
                Point2D p2=toMapPoint((int)r.getMaxX(),(int)r.getMaxY());
506
                rect.setFrameFromDiagonal(p1,p2);
507
                return rect;
508
        }
509

    
510
        /**
511
         * <p>Converts and returns the distance <code>d</code>, that is in <i>screen
512
         *  coordinates</i> to <i>map coordinates</i> using the transformation affine information
513
         *  in the {@link #trans #trans} attribute.</p>
514
         *
515
         * @param d distance in pixels
516
         *
517
         * @return distance equivalent in <i>map coordinates</i>
518
         *
519
         * @see #fromMapDistance(double)
520
         * @see AffineTransform
521
         */
522
        public double toMapDistance(int d) {
523
                double dist = d / trans.getScaleX();
524

    
525
                return dist;
526
        }
527

    
528
        /**
529
         * <p>Converts and returns the 2D point argument, that is in <i>screen coordinates</i>
530
         *  (pixels) to <i>map coordinates</i> using the
531
         *  inverse affine transformation of the {@link #trans #trans} attribute.</p>
532
         *
533
         * @param pScreen the 2D point in <i>screen coordinates</i> (pixels)
534
         *
535
         * @return 2D point equivalent in <i>map coordinates</i>
536
         *
537
         * @see #toMapPoint(int, int)
538
         * @see AffineTransform#createInverse()
539
         * @see AffineTransform#transform(Point2D, Point2D)
540
         */
541
        public Point2D toMapPoint(Point2D pScreen) {
542
                Point2D.Double pWorld = new Point2D.Double();
543
                AffineTransform at;
544

    
545
                try {
546
                        at = trans.createInverse();
547
                        at.transform(pScreen, pWorld);
548
                } catch (NoninvertibleTransformException e) {
549
                        throw new RuntimeException("Non invertible transform Exception",e);
550
                }
551

    
552
                return pWorld;
553
        }
554

    
555
        /**
556
         * <p>Returns the real distance (in <i>world coordinates</i>) at the graphic layers of two 2D points
557
         *  (in <i>map coordinates</i>) of the plane where is selected the <i>extent</i>.</p>
558
         * <p>If the projection of this view is UTM, considers the Earth curvature.</p>
559
         *
560
         * @param pt1 a 2D point in <i>map coordinates</i>
561
         * @param pt2 another 2D point in <i>map coordinates</i>
562
         *
563
         * @return the distance in meters between the two points 2D
564
         *
565
         * @see GeoCalc#distanceVincenty(Point2D, Point2D)
566
         */
567
        public double distanceWorld(Point2D pt1, Point2D pt2) {
568
                double dist = -1;
569
                dist = pt1.distance(pt2);
570

    
571
                if ((proj != null) && !(proj.isProjected())) {
572
                        dist = new GeoCalc(proj).distanceVincenty(proj.toGeo(pt1),
573
                                        proj.toGeo(pt2));
574
                        return dist;
575
                }
576
                return (dist*MapContext.getDistanceTrans2Meter()[getMapUnits()]);
577
        }
578

    
579
        /**
580
         * <p>Sets as extent and adjusted extent of this view port, the previous. Recalculating
581
         *  its parameters.</p>
582
         *
583
         * @see #getExtents()
584
         * @see #calculateAffineTransform()
585
         */
586
        public void setPreviousExtent() {
587
                this.updateDrawVersion();
588
                extent = extents.removePrev();
589

    
590
                //Calcula la transformaci?n af?n
591
                calculateAffineTransform();
592

    
593
                // Lanzamos los eventos de extent cambiado
594
                callExtentChanged(getAdjustedExtent());
595
        }
596

    
597
        /**
598
         * <p>Gets the area selected by user using some tool.</p>
599
         *
600
         * <p>When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom out</i> tools,
601
         *  but also zooming to a selected feature or shape) the extent that covers that
602
         *  area is the value returned by this method. It is not the actual area shown
603
         *  because it doesn't care about the aspect ratio of the image size of the view. However, any
604
         *  part of the real world contained in this extent is shown in the view.</p>
605
         *
606
         * <p>If you are looking for the complete extent currently shown, you must use the
607
         *  {@linkplain #getAdjustedExtent()} method.</p>
608
         *
609
         * @return the current extent
610
         *
611
         * @see #setExtent(Rectangle2D)
612
         * @see #getAdjustedExtent()
613
         * @see #setPreviousExtent()
614
         * @see #getExtents()
615
         */
616
        public Rectangle2D getExtent() {
617
                return extent;
618
        }
619

    
620
        /**
621
         * <p>Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
622
         * <ul>
623
         * <li>Stores the previous extent.
624
         * <li>Calculates the new extent using <code>r</code>:
625
         * <pre>extent = new Rectangle2D.Double(r.getMinX() - 0.1, r.getMinY() - 0.1, r.getWidth() + 0.2, r.getHeight() + 0.2);</pre>
626
         * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new scale, adjusted extent, affine transformation between
627
         *  map and screen coordinates, the real world coordinates equivalent to 1 pixel, and the real world coordinates equivalent to 3 pixels.
628
         * <li>Notifies all {@link ViewPortListener ViewPortListener} registered that the extent has changed.
629
         * </ul>
630
         * </p>
631
         *
632
         * @param r the new extent
633
         *
634
         * @see #getExtent()
635
         * @see #getExtents()
636
         * @see #calculateAffineTransform()
637
         * @see #setPreviousExtent()
638
         */
639
        public void setExtent(Rectangle2D r) {
640
                Rectangle2D newExtent=null;
641

    
642
                //Esto comprueba que el extent no es de anchura o altura = "0"
643
                //y si es as? lo redimensiona.
644
                if (r!=null &&((r.getWidth() == 0) || (r.getHeight() == 0))) {
645
                        newExtent = new Rectangle2D.Double(r.getMinX() - 0.1,
646
                                        r.getMinY() - 0.1, r.getWidth() + 0.2, r.getHeight() + 0.2);
647
                } else {
648
                        newExtent = r;
649
                }
650
                if (this.extent != null && this.extent.equals(newExtent)){
651
                        return;
652
                }
653
                if (extent != null) {
654
                        extents.put(extent);
655
                }
656
                this.updateDrawVersion();
657
                this.extent = newExtent;
658

    
659
                //Calcula la transformaci?n af?n
660
                calculateAffineTransform();
661

    
662
                // Lanzamos los eventos de extent cambiado
663
                callExtentChanged(getAdjustedExtent());
664
        }
665

    
666
        /**
667
         * <p>Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
668
         * <ul>
669
         * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new scale, adjusted extent, affine transformation between
670
         *  map and screen coordinates, the real world coordinates equivalent to 1 pixel, and the real world coordinates equivalent to 3 pixels.
671
         * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered that the extent has changed.
672
         * </ul>
673
         * </p>
674
         *
675
         * @see #setExtent(Rectangle2D)
676
         * @see #calculateAffineTransform()
677
         */
678
        public void refreshExtent() {
679
                //this.scale = scale;
680

    
681
                //Calcula la transformaci?n af?n
682
                calculateAffineTransform();
683

    
684
                // Lanzamos los eventos de extent cambiado
685
                callExtentChanged(getAdjustedExtent());
686
        }
687

    
688
        /**
689
         * <p>Calculates and returns using the current projection of this view port, the scale that
690
         *  is the extent in <i>screen coordinates</i> from the image in <i>map coordinates</i>.</p>
691
         *
692
         * @return the scale <i>extent / image size</i> projected by this view port
693
         *
694
         * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
695
         */
696
        public double getScale() {
697
                return proj.getScale(extent.getMinX(), extent.getMaxX(),
698
                        imageSize.getWidth(), dpi);
699
        }
700

    
701
        /**
702
         * <p>Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D coordinates</i> (pixels),
703
         * preserving the "straightness" and "parallelism" of the lines.</p>
704
         *
705
         * @return the affine transformation
706
         *
707
         * @see #setAffineTransform(AffineTransform)
708
         * @see #calculateAffineTransform()
709
         */
710
        public AffineTransform getAffineTransform() {
711
                return trans;
712
        }
713

    
714
        /**
715
         * <p>Returns the size of the image projected.</p>
716
         *
717
         * @return the image size
718
         *
719
         * @see #setImageSize(Dimension)
720
         * @see #getImageHeight()
721
         * @see #getImageWidth()
722
         */
723
        public Dimension getImageSize() {
724
                return imageSize;
725
        }
726

    
727
        /**
728
         * <p>Sets the size of the image projected, recalculating the parameters of this view port.</p>
729
         *
730
         * @param imageSize the image size
731
         *
732
         * @see #getImageSize()
733
         * @see #calculateAffineTransform()
734
         */
735
        public void setImageSize(Dimension imageSize) {
736
                if (this.imageSize == null  || (!this.imageSize.equals(imageSize))){
737
                        this.updateDrawVersion();
738
                        this.imageSize = imageSize;
739
                        calculateAffineTransform();
740
                }
741
        }
742

    
743
        /**
744
         * <p>Notifies to all view port listeners registered, that the adjusted extent of this view port
745
         *  has changed.</p>
746
         *
747
         * @param newRect the new adjusted extend
748
         *
749
         * @see #refreshExtent()
750
         * @see #setExtent(Rectangle2D)
751
         * @see #setPreviousExtent()
752
         * @see ExtentEvent
753
         * @see ViewPortListener
754
         */
755
        protected void callExtentChanged(Rectangle2D newRect) {
756
                ExtentEvent ev = ExtentEvent.createExtentEvent(newRect);
757

    
758
                for (int i = 0; i < listeners.size(); i++) {
759
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
760
                        listener.extentChanged(ev);
761
                }
762
        }
763

    
764
        /**
765
         * <p>Notifies to all view port listeners registered, that the background color of this view port
766
         *  has changed.</p>
767
         *
768
         * @param c the new background color
769
         *
770
         * @see #setBackColor(Color)
771
         * @see ColorEvent
772
         * @see ViewPortListener
773
         */
774
        private void callColorChanged(Color c) {
775
                ColorEvent ce = ColorEvent.createColorEvent(c);
776

    
777
                for (int i = 0; i < listeners.size(); i++) {
778
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
779
                        listener.backColorChanged(ce);
780
                }
781
        }
782

    
783
        /**
784
         * <p>Notifies to all view port listeners registered, that the projection of this view port
785
         *  has changed.</p>
786
         *
787
         * @param projection the new projection
788
         *
789
         * @see #setProjection(IProjection)
790
         * @see ProjectionEvent
791
         * @see ViewPortListener
792
         */
793
        private void callProjectionChanged(IProjection projection) {
794
                ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
795

    
796
                for (int i = 0; i < listeners.size(); i++) {
797
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
798
                        listener.projectionChanged(ev);
799
                }
800
        }
801

    
802
        /**
803
         * <p>Calculates the affine transformation between the {@link #extent extent} in <i>map 2D coordinates</i> to
804
         *  the image area in the screen, in <i>screen 2D coordinates</i> (pixels).</p>
805
         *
806
         * <p>This process recalculates some parameters of this view port:<br>
807
         *
808
         * <ul>
809
         * <li>The new {@link #scale scale} .
810
         * <li>The new {@link #adjustedExtent adjustedExtent} .
811
         * <li>The new {@link #trans trans} .
812
         * <li>The new real world coordinates equivalent to 1 pixel ({@link #dist1pixel dist1pixel}) .
813
         * <li>The new real world coordinates equivalent to 3 pixels ({@link #dist3pixel dist3pixel}) .
814
         * </ul>
815
         * </p>
816
         *
817
         * @see #getAffineTransform()
818
         * @see #setAffineTransform(AffineTransform)
819
         * @see #refreshExtent()
820
         * @see #setExtent(Rectangle2D)
821
         * @see #setImageSize(Dimension)
822
         * @see #setPreviousExtent()
823
         * @see #createFromXML(XMLEntity)
824
         * @see AffineTransform
825
         */
826
        private void calculateAffineTransform() {
827
                if ((imageSize == null) || (extent == null) ||
828
                                (imageSize.getWidth() <= 0) || (imageSize.getHeight() <= 0)) {
829
                        return;
830
                }
831

    
832
                AffineTransform escalado = new AffineTransform();
833
                AffineTransform translacion = new AffineTransform();
834

    
835
                double escalaX;
836
                double escalaY;
837

    
838
                escalaX = imageSize.getWidth() / extent.getWidth();
839
                escalaY = imageSize.getHeight() / extent.getHeight();
840

    
841
                double xCenter = extent.getCenterX();
842
                double yCenter = extent.getCenterY();
843
                double newHeight;
844
                double newWidth;
845

    
846
                adjustedExtent = new Rectangle2D.Double();
847

    
848
                if (adjustableExtent) {
849
                        if (escalaX < escalaY) {
850
                                scale = escalaX;
851
                                newHeight = imageSize.getHeight() / scale;
852
                                adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0),
853
                                        yCenter - (newHeight / 2.0), extent.getWidth(), newHeight);
854
                        } else {
855
                                scale = escalaY;
856
                                newWidth = imageSize.getWidth() / scale;
857
                                adjustedExtent.setRect(xCenter - (newWidth / 2.0),
858
                                        yCenter - (extent.getHeight() / 2.0), newWidth,
859
                                        extent.getHeight());
860
                        }
861
                        escalado.setToScale(scale, -scale);
862
                }
863
                else { // adjusted is same as extent
864
                        scale = escalaX;
865
                        adjustedExtent.setFrame(extent);
866
                        escalado.setToScale(escalaX, -escalaY);
867
                }
868

    
869
                translacion.setToTranslation(-getAdjustedExtent().getX(),
870
                        -getAdjustedExtent().getY() - getAdjustedExtent().getHeight());
871

    
872
                AffineTransform offsetTrans = new AffineTransform();
873
                offsetTrans.setToTranslation(offset.getX(), offset.getY());
874

    
875
                trans.setToIdentity();
876
                trans.concatenate(offsetTrans);
877
                trans.concatenate(escalado);
878

    
879
                trans.concatenate(translacion);
880

    
881
                // Calculamos las distancias de 1 pixel y 3 pixel con esa transformaci?n
882
                // de coordenadas, de forma que est?n precalculadas para cuando las necesitemos
883
                AffineTransform at;
884

    
885
                try {
886
                        at = trans.createInverse();
887

    
888
                        java.awt.Point pPixel = new java.awt.Point(1, 1);
889
                        Point2D.Float pProv = new Point2D.Float();
890
                        at.deltaTransform(pPixel, pProv);
891

    
892
                        dist1pixel = pProv.x;
893
                        dist3pixel = 3 * pProv.x;
894
                } catch (NoninvertibleTransformException e) {
895
                        System.err.println("transformada afin = " + trans.toString());
896
                        System.err.println("extent = " + extent.toString() +
897
                                " imageSize= " + imageSize.toString());
898
                        throw new RuntimeException("Non invertible transform Exception",e);
899
                }
900
        }
901

    
902
        /**
903
         * <p>Sets the offset.</p>
904
         * <p>The offset is the position where start drawing the map.</p>
905
         *
906
         * @param p 2D point that represents the offset in pixels
907
         *
908
         * @see #getOffset()
909
         */
910
        public void setOffset(Point2D p) {
911
                if (!offset.equals(p)){
912
                        this.updateDrawVersion();
913
                        offset = p;
914
                }
915
        }
916

    
917
        /**
918
         * <p>Gets the offset.</p>
919
         * <p>The offset is the position where start drawing the map.</p>
920
         *
921
         * @return 2D point that represents the offset in pixels
922
         *
923
         * @see #setOffset(Point2D)
924
         */
925
        public Point2D getOffset() {
926
                return offset;
927
        }
928

    
929
        /**
930
         * <p>Sets the background color.</p>
931
         *
932
         * @param c the new background color
933
         *
934
         * @see #getBackColor()
935
         */
936
        public void setBackColor(Color c) {
937
                if (!c.equals(this.backColor)){
938
                        this.updateDrawVersion();
939
                        backColor = c;
940
                        callColorChanged(backColor);
941
                }
942
        }
943

    
944
        /**
945
         * <p>Gets the background color.</p>
946
         *
947
         * @return the background color of the view
948
         *
949
         * @see #setBackColor(Color)
950
         */
951
        public Color getBackColor() {
952
                return backColor;
953
        }
954

    
955
        /**
956
         * <p>Returns the extent currently covered by the view adjusted (scaled) to the image size aspect.</p>
957
         *
958
         * @return extent of the view adjusted to the image size aspect
959
         *
960
         * @see #setAdjustable(boolean)
961
         */
962
        public Rectangle2D getAdjustedExtent() {
963
                if (cliprect!=null){
964
                        return adjustedExtent.createIntersection(cliprect);
965
                }
966
                return adjustedExtent;
967
        }
968

    
969
        /**
970
         * <p>Returns the measurement unit of this view port used for measuring distances and displaying information.</p>
971
         *
972
         * @return the measurement unit of this view used for measuring distances and displaying information
973
         *
974
         * @see #setDistanceUnits(int)
975
         */
976
        public int getDistanceUnits() {
977
                return distanceUnits;
978
        }
979
        /**
980
         * <p>Returns the measurement unit of this view port used for measuring areas and displaying information.</p>
981
         *
982
         * @return the measurement unit of this view used for measuring areas and displaying information
983
         *
984
         * @see #setDistanceUnits(int)
985
         */
986
        public int getDistanceArea() {
987
                return distanceArea;
988
        }
989
        /**
990
         * <p>Sets the measurement unit of this view port used for measuring distances and displaying information.</p>
991
         *
992
         * @param distanceUnits the measurement unit of this view used for measuring distances and displaying information
993
         *
994
         * @see #getDistanceUnits()
995
         */
996
        public void setDistanceUnits(int distanceUnits) {
997
                this.distanceUnits = distanceUnits;
998
        }
999
        /**
1000
         * <p>Sets the measurement unit of this view port used for measuring areas and displaying information.</p>
1001
         *
1002
         * @param distanceUnits the measurement unit of this view used for measuring areas and displaying information
1003
         *
1004
         * @see #getDistanceUnits()
1005
         */
1006
        public void setDistanceArea(int distanceArea) {
1007
                this.distanceArea = distanceArea;
1008
        }
1009
        /**
1010
         * <p>Gets the measurement unit used by this view port for the map.</p>
1011
         *
1012
         * @return Returns the current map measure unit
1013
         *
1014
         * @see #setMapUnits(int)
1015
         */
1016
        public int getMapUnits() {
1017
                return mapUnits;
1018
        }
1019

    
1020
        /**
1021
         * <p>Sets the measurement unit used by this view port for the map.</p>
1022
         *
1023
         * @param mapUnits the new map measure unit
1024
         *
1025
         * @see #getMapUnits()
1026
         */
1027
        public void setMapUnits(int mapUnits) {
1028
                this.mapUnits = mapUnits;
1029
        }
1030

    
1031
        /**
1032
         * <p>Gets the width in <i>screen coordinates</i> of the rectangle where the image is displayed.</p>
1033
         * <p>Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1034
           *
1035
         * <ul>
1036
         * <li>The new {@link #scale scale} .
1037
         * <li>The new {@link #adjustedExtent adjustableExtent} .
1038
         * <li>The new {@link #trans trans} .
1039
         * <li>The new real world coordinates equivalent to 1 pixel ({@link #dist1pixel dist1pixel}) .
1040
         * <li>The new real world coordinates equivalent to 3 pixels ({@link #dist3pixel dist3pixel}) .
1041
         * </ul>
1042
         * </p>
1043
         *
1044
         * @see #getImageHeight()
1045
         * @see #getImageSize()
1046
         * @see #setImageSize(Dimension)
1047
         */
1048
        public int getImageWidth() {
1049
                return imageSize.width;
1050
        }
1051

    
1052
        /**
1053
         * <p>Gets the height in <i>screen coordinates</i> of the rectangle where the image is displayed.</p>
1054
         * <p>Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1055
           *
1056
         * <ul>
1057
         * <li>The new {@link #scale scale} .
1058
         * <li>The new {@link #adjustedExtent adjustableExtent} .
1059
         * <li>The new {@link #trans trans} .
1060
         * <li>The new real world coordinates equivalent to 1 pixel ({@link #dist1pixel dist1pixel}) .
1061
         * <li>The new real world coordinates equivalent to 3 pixels ({@link #dist3pixel dist3pixel}) .
1062
         * </ul>
1063
         * </p>
1064
         *
1065
         * @see #getImageWidth()
1066
         * @see #getImageSize()
1067
         * @see #setImageSize(Dimension)
1068
         */
1069
        public int getImageHeight() {
1070
                return imageSize.height;
1071
        }
1072

    
1073
        /**
1074
         * <p>Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in the view with the current extent.</p>
1075
         *
1076
         * @return the distance
1077
         *
1078
         * @see #setDist1pixel(double)
1079
         */
1080
        public double getDist1pixel() {
1081
                return dist1pixel;
1082
        }
1083

    
1084
        /**
1085
         * <p>Sets the distance in <i>world coordinates</i> equivalent to 1 pixel in the view with the current extent.</p>
1086
         *
1087
         * @param dist1pixel the distance
1088
         *
1089
         * @see #getDist1pixel()
1090
         */
1091
        public void setDist1pixel(double dist1pixel) {
1092
                if (dist1pixel == this.dist1pixel){
1093
                        return;
1094
                }
1095
                this.updateDrawVersion();
1096
                this.dist1pixel = dist1pixel;
1097
        }
1098

    
1099
        /**
1100
         * <p>Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in the view with the current extent.</p>
1101
         *
1102
         * @return the distance
1103
         *
1104
         * @see #setDist3pixel(double)
1105
         */
1106
        public double getDist3pixel() {
1107
                return dist3pixel;
1108
        }
1109

    
1110
        /**
1111
         * <p>Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in the view with the current extent.</p>
1112
         *
1113
         * @param dist3pixel the distance
1114
         *
1115
         * @see #getDist3pixel()
1116
         */
1117
        public void setDist3pixel(double dist3pixel) {
1118
                if (this.dist3pixel == dist3pixel){
1119
                        return;
1120
                }
1121
                this.updateDrawVersion();
1122
                this.dist3pixel = dist3pixel;
1123
        }
1124

    
1125
        /**
1126
         * <p>Returns the last previous extents of this view port.</p>
1127
         *
1128
         * @return the last previous extents of this view port
1129
         *
1130
         * @see #setPreviousExtent()
1131
         */
1132
        public ExtentHistory getExtents() {
1133
                return extents;
1134
        }
1135

    
1136
        /**
1137
         * <p>Gets the projection used in this view port.</p>
1138
         *
1139
         * @return projection used in this view port
1140
         *
1141
         * @see #setProjection(IProjection)
1142
         */
1143
        public IProjection getProjection() {
1144
                return proj;
1145
        }
1146

    
1147
        /**
1148
         * <p>Sets the projection to this view port.</p>
1149
         *
1150
         * @param proj the new projection
1151
         *
1152
         * @see #getProjection()
1153
         */
1154
        public void setProjection(IProjection proj) {
1155
                if(this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1156
                        this.updateDrawVersion();
1157
                        this.proj = proj;
1158
                        callProjectionChanged(proj);
1159
                }
1160
        }
1161

    
1162
//  -----------------------------------------------------------------------------------------------------------
1163
//  NOTA PARA DESARROLLADORES SOBRE EL M?TODO "public void setAffineTransform(AffineTransform at)"
1164
//  ==============================================================================================
1165
//          Only used for print, should be removed, redefining the {@link RasterAdapter RasterAdapter} interface,
1166
//           allowing it to receive a {@link ViewPortData ViewPortData} .
1167
//  -----------------------------------------------------------------------------------------------------------
1168

    
1169
        /**
1170
         * <p>Sets only the affine transform to this view port, without updating dependent attributes.</p>
1171
         * <p><b><i>This method could be problematic!</i></b></p>
1172
         *
1173
         * @param at the affine transform to set
1174
         *
1175
         * @see #getAffineTransform()
1176
         * @see #calculateAffineTransform()
1177
         */
1178
        public void setAffineTransform(AffineTransform at)
1179
        {
1180
            this.trans = at;
1181
        }
1182

    
1183
        /**
1184
         * <p>Returns an XML entity that represents this view port instance:<br>
1185
         * <ul>
1186
         * <li>Properties:
1187
         *  <ul>
1188
         *  <li><i>className</i>: name of this class.
1189
         *  <li>If defined, the adjusted extent:
1190
         *   <ul>
1191
         *   <li><i>adjustedExtentX</i>: X coordinate of the adjusted extent.
1192
         *   <li><i>adjustedExtentY</i>: Y coordinate of the adjusted extent.
1193
         *   <li><i>adjustedExtentW</i>: width of the adjusted extent.
1194
         *   <li><i>adjustedExtentH</i>: height of the adjusted extent.
1195
         *   </ul>
1196
         *  <li>If defined, the background color:
1197
         *   <ul>
1198
         *   <li><i>backColor</i>: background color.
1199
         *   </ul>
1200
         *  <li>If defined, the clip:
1201
         *   <ul>
1202
         *   <li><i>clipX</i>: X coordinate of the clip.
1203
         *   <li><i>clipY</i>: Y coordinate of clip.
1204
         *   <li><i>clipW</i>: width of the clip.
1205
         *   <li><i>clipH</i>: height of the clip.
1206
         *   </ul>
1207
         *  <li><i>dist1pixel</i>: the distance in world coordinates equivalent to 1 pixel in the view.
1208
         *  <li><i>dist3pixel</i>: the distance in world coordinates equivalent to 3 pixels in the view.
1209
         *  <li><i>distanceUnits</i>: the distance measurement unit.
1210
         *  <li>If defined, the extent:
1211
         *   <ul>
1212
         *   <li><i>extentX</i>: X coordinate of the extent.
1213
         *   <li><i>extentY</i>: Y coordinate of the extent.
1214
         *   <li><i>extentW</i>: width of the extent.
1215
         *   <li><i>extentH</i>: height of the extent.
1216
         *   </ul>
1217
         *  <li><i>mapUnits</i>: the map measurement unit.
1218
         *  <li><i>offsetX</i>: X coordinate of the offset.
1219
         *  <li><i>offsetY</i>: Y coordinate of the offset.
1220
         *  <li>If defined, the projection:
1221
         *   <ul>
1222
         *   <li>If its defined, the projection:
1223
         *    <ul>
1224
         *     <li><i>proj</i>: the projection.</li>
1225
         *    </ul>
1226
         *   </ul>
1227
         *  <li><i>scale</i>: ratio between the size of <code>imageSize</code> and <code>extent</code>.
1228
         *  </ul>
1229
         * <li>Child branches:
1230
         *  <ul>
1231
         *  <li>XML entity of the internal {@link ExtentHistory ExtentHistory} .
1232
         *  </ul>
1233
         * </ul>
1234
         *
1235
         * @return the XML entity
1236
         *
1237
         * @see #createFromXML(XMLEntity)
1238
         */
1239
        public XMLEntity getXMLEntity() {
1240
                XMLEntity xml = new XMLEntity();
1241
                xml.putProperty("className",this.getClass().getName());
1242

    
1243
                if (adjustedExtent != null) {
1244
                        xml.putProperty("adjustedExtentX", adjustedExtent.getX());
1245
                        xml.putProperty("adjustedExtentY", adjustedExtent.getY());
1246
                        xml.putProperty("adjustedExtentW", adjustedExtent.getWidth());
1247
                        xml.putProperty("adjustedExtentH", adjustedExtent.getHeight());
1248
                }
1249

    
1250
                if (backColor != null)
1251
                    xml.putProperty("backColor", StringUtilities.color2String(backColor));
1252

    
1253
                if (clip != null) {
1254
                        xml.putProperty("clipX", clip.getX());
1255
                        xml.putProperty("clipY", clip.getY());
1256
                        xml.putProperty("clipW", clip.getWidth());
1257
                        xml.putProperty("clipH", clip.getHeight());
1258
                }
1259

    
1260
                xml.putProperty("dist1pixel", dist1pixel);
1261
                xml.putProperty("dist3pixel", dist3pixel);
1262
                xml.putProperty("distanceUnits", distanceUnits);
1263

    
1264
                if (extent != null) {
1265
                        xml.putProperty("extentX", extent.getX());
1266
                        xml.putProperty("extentY", extent.getY());
1267
                        xml.putProperty("extentW", extent.getWidth());
1268
                        xml.putProperty("extentH", extent.getHeight());
1269
                }
1270

    
1271
                xml.addChild(extents.getXMLEntity());
1272
                xml.putProperty("mapUnits", mapUnits);
1273
                xml.putProperty("offsetX", offset.getX());
1274
                xml.putProperty("offsetY", offset.getY());
1275

    
1276
                if (proj != null) {
1277
                        xml.putProperty("proj", proj.getAbrev());
1278
                }
1279

    
1280
                xml.putProperty("scale", scale);
1281
                xml.putProperty("zoomFactor", getZoomFactor());
1282

    
1283
                return xml;
1284
        }
1285

    
1286
        /**
1287
         * <p>Creates a new <code>ViewPort</code> from an XML entity.</p>
1288
         *
1289
         * @param xml an XML entity
1290
         *
1291
         * @return the new <code>ViewPort</code>
1292
         *
1293
         * @see #getXMLEntity()
1294
         * @see #createFromXML(XMLEntity)
1295
         */
1296
        public static ViewPort createFromXML03(XMLEntity xml) {
1297
                ViewPort vp = new ViewPort(null);
1298

    
1299
                if (xml.contains("adjustedExtentX")) {
1300
                        vp.adjustedExtent = new Rectangle2D.Double(xml.getDoubleProperty(
1301
                                                "adjustedExtentX"),
1302
                                        xml.getDoubleProperty("adjustedExtentY"),
1303
                                        xml.getDoubleProperty("adjustedExtentW"),
1304
                                        xml.getDoubleProperty("adjustedExtentH"));
1305
                }
1306

    
1307
                if (xml.contains("backColor")) {
1308
                        vp.setBackColor(StringUtilities.string2Color(xml.getStringProperty(
1309
                                                "backColor")));
1310
                }
1311

    
1312
                if (xml.contains("clipX")) {
1313
                        vp.clip = new Rectangle2D.Double(xml.getDoubleProperty("clipX"),
1314
                                        xml.getDoubleProperty("clipY"),
1315
                                        xml.getDoubleProperty("clipW"),
1316
                                        xml.getDoubleProperty("clipH"));
1317
                }
1318

    
1319
                vp.setDist1pixel(xml.getDoubleProperty("dist1pixel"));
1320
                vp.setDist3pixel(xml.getDoubleProperty("dist3pixel"));
1321
                vp.setDistanceUnits(xml.getIntProperty("distanceUnits"));
1322
                vp.extents = ExtentHistory.createFromXML03(xml.getChild(0));
1323

    
1324
                if (xml.contains("extentX")) {
1325
                        vp.setExtent(new Rectangle2D.Double(xml.getDoubleProperty("extentX"),
1326
                                        xml.getDoubleProperty("extentY"),
1327
                                        xml.getDoubleProperty("extentW"),
1328
                                        xml.getDoubleProperty("extentH")));
1329

    
1330
                        //Calcula la transformaci?n af?n
1331
                        vp.calculateAffineTransform();
1332

    
1333
                        // Lanzamos los eventos de extent cambiado
1334
                        // vp.callExtentListeners(vp.adjustedExtent);
1335
                }
1336

    
1337
                vp.setMapUnits(xml.getIntProperty("mapUnits"));
1338
                vp.setOffset(new Point2D.Double(xml.getDoubleProperty("offsetX"),
1339
                                xml.getDoubleProperty("offsetY")));
1340

    
1341
                if (xml.contains("proj")) {
1342
                        vp.proj = CRSFactory.getCRS(xml.getStringProperty("proj"));
1343
                }
1344

    
1345
                //vp.setScale(xml.getDoubleProperty("scale"));
1346
                vp.refreshExtent();
1347
                return vp;
1348
        }
1349

    
1350
        /**
1351
         * <p>Creates a new <code>ViewPort</code> from an XML entity.</p>
1352
         *
1353
         * @param xml an XML entity
1354
         *
1355
         * @return the new <code>ViewPort</code>
1356
         *
1357
         * @see #getXMLEntity()
1358
         * @see #createFromXML03(XMLEntity)
1359
         */
1360
        public static ViewPort createFromXML(XMLEntity xml) {
1361
                ViewPort vp = new ViewPort(null);
1362

    
1363
                if (xml.contains("adjustedExtentX")) {
1364
                        vp.adjustedExtent = new Rectangle2D.Double(xml.getDoubleProperty(
1365
                                                "adjustedExtentX"),
1366
                                        xml.getDoubleProperty("adjustedExtentY"),
1367
                                        xml.getDoubleProperty("adjustedExtentW"),
1368
                                        xml.getDoubleProperty("adjustedExtentH"));
1369
                }
1370

    
1371
                if (xml.contains("backColor")) {
1372
                        vp.setBackColor(StringUtilities.string2Color(xml.getStringProperty(
1373
                                                "backColor")));
1374
                }else {
1375
                        vp.setBackColor(Color.white);
1376
                }
1377

    
1378
                if (xml.contains("clipX")) {
1379
                        vp.clip = new Rectangle2D.Double(xml.getDoubleProperty("clipX"),
1380
                                        xml.getDoubleProperty("clipY"),
1381
                                        xml.getDoubleProperty("clipW"),
1382
                                        xml.getDoubleProperty("clipH"));
1383
                }
1384

    
1385
                vp.setDist1pixel(xml.getDoubleProperty("dist1pixel"));
1386
                vp.setDist3pixel(xml.getDoubleProperty("dist3pixel"));
1387
                vp.setDistanceUnits(xml.getIntProperty("distanceUnits"));
1388
                if (xml.contains("distanceArea")){
1389
                        vp.setDistanceArea(xml.getIntProperty("distanceArea"));
1390
                }else{
1391
                        vp.setDistanceArea(xml.getIntProperty("distanceUnits"));
1392
                }
1393
                vp.extents = ExtentHistory.createFromXML(xml.getChild(0));
1394

    
1395
                if (xml.contains("extentX")) {
1396
                        vp.setExtent(new Rectangle2D.Double(xml.getDoubleProperty("extentX"),
1397
                                        xml.getDoubleProperty("extentY"),
1398
                                        xml.getDoubleProperty("extentW"),
1399
                                        xml.getDoubleProperty("extentH")));
1400

    
1401
                        //Calcula la transformaci?n af?n
1402
                        vp.calculateAffineTransform();
1403

    
1404
                        // Lanzamos los eventos de extent cambiado
1405
                        // vp.callExtentListeners(vp.adjustedExtent);
1406
                }
1407

    
1408
                vp.setMapUnits(xml.getIntProperty("mapUnits"));
1409
                vp.setOffset(new Point2D.Double(xml.getDoubleProperty("offsetX"),
1410
                                xml.getDoubleProperty("offsetY")));
1411

    
1412
                if (xml.contains("proj")) {
1413
                        vp.proj = CRSFactory.getCRS(xml.getStringProperty("proj"));
1414
                }
1415

    
1416
                if (xml.contains("zoomFactor")) {
1417
                        vp.setZoomFactor(xml.getDoubleProperty("zoomFactor"));
1418
                }
1419

    
1420
                //vp.setScale(xml.getDoubleProperty("scale"));
1421
                vp.refreshExtent();
1422
                return vp;
1423
        }
1424

    
1425
        /**
1426
         * <p>Fast clone implementation: creates and returns a clone of this view port using XML entities.</p>
1427
         * <p>Isn't a <i>deepclone</i> to avoid unnecessary memory consumption.</p>
1428
         *
1429
         * @return the new view port
1430
         *
1431
         * @see #createFromXML(XMLEntity)
1432
         */
1433
        public ViewPort cloneViewPort() {
1434
                return createFromXML(getXMLEntity());
1435
        }
1436

    
1437
        /**
1438
         * <p>Returns a <code>String</code> representation of the main values of this view port: <code>{@linkplain #extent}</code>,
1439
         *  <code>{@linkplain #adjustedExtent}</code>, <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>, and <code>{@linkplain #trans}</code>.</p>
1440
         *
1441
         * @return a <code>string</code> representation of the main values of this view port
1442
         */
1443
        public String toString() {
1444

    
1445
                String str;
1446
                str = "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent=" +
1447
                        adjustedExtent + "\nimageSize=" + imageSize + "\nescale=" + scale +
1448
                        "\ntrans=" + trans;
1449

    
1450
                return str;
1451
        }
1452

    
1453
        /**
1454
         * <p>Sets the position and size of the clipping rectangle.</p>
1455
         *
1456
         * @param rectView the clipping rectangle to set
1457
         */
1458
        public void setClipRect(Rectangle2D rectView) {
1459
                this.updateDrawVersion();
1460
                cliprect=rectView;
1461
        }
1462

    
1463
        /**
1464
         * <p>Converts and returns the {@link Rectangle2D Rectangle2D}, that is in <i>map
1465
         *  coordinates</i> to <i>screen coordinates</i> (pixels) using an <i>inverse transform</i> with
1466
         *  the transformation affine information in the {@link #trans #trans} attribute.</p>
1467
         *
1468
         * @param r the 2D rectangle in <i>map coordinates</i>
1469
         * @return 2D rectangle equivalent in <i>screen coordinates</i> (pixels)
1470
         *
1471
         * @see #toMapRectangle(Rectangle2D)
1472
         * @see #fromMapDistance(double)
1473
         * @see #fromMapPoint(Point2D)
1474
         */
1475
        public Rectangle2D fromMapRectangle(Rectangle2D r) {
1476
                Rectangle2D rect=new Rectangle2D.Double();
1477
                Point2D p1=fromMapPoint((int)r.getX(),(int)r.getY());
1478
                Point2D p2=fromMapPoint((int)r.getMaxX(),(int)r.getMaxY());
1479
                rect.setFrameFromDiagonal(p1,p2);
1480
                return rect;
1481
        }
1482

    
1483
        /**
1484
         * <p>Recalculates the current <code>{@linkplain #extent}</code> using an scale. It's necessary execute {@linkplain #refreshExtent()} after.</p>
1485
         *
1486
         * @param s the scale to set
1487
         *
1488
         * @deprecated since 07/09/07, use {@linkplain MapContext#setScaleView(long)}
1489
         */
1490
        public void setScale(long s){
1491
                double x=extent.getX();
1492
                double y=extent.getY();
1493
                double escalaX = imageSize.getWidth() / extent.getWidth();
1494
                double w=imageSize.getWidth() / s;
1495
                double h=imageSize.getHeight() / s;
1496
                double difw = escalaX/s;
1497

    
1498
                double x1 = (-x * difw) -
1499
            x+
1500
            extent.getWidth()/2;
1501
        double y1 = (-y * difw) -
1502
            y +
1503
            extent.getHeight()/2;
1504
        double w1=extent.getWidth()*difw;
1505
        double h1=extent.getHeight()*difw;
1506
                extent.setRect(-x1,-y1,w1,h1);
1507
        }
1508

    
1509

    
1510
        public long getDrawVersion() {
1511
                return this.drawVersion;
1512
        }
1513

    
1514
        protected void updateDrawVersion(){
1515
                this.drawVersion++;
1516
        }
1517
        
1518
        
1519
        private double zoomFactor = 1d;
1520

    
1521
        public double getZoomFactor() {
1522
                return zoomFactor;
1523
        }
1524

    
1525
        public void setZoomFactor(double z) {
1526
                this.zoomFactor = z;
1527
        }
1528
        
1529

    
1530
}