Statistics
| Revision:

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

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

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

    
181
        /**
182
         * <p>
183
         * Screen resolution in <i>dots-per-inch</i>. Useful to calculate the
184
         * geographic scale of the view.
185
         * </p>
186
         * 
187
         * @see Toolkit#getScreenResolution()
188
         * @see #getScale()
189
         */
190
        private static int dpi = java.awt.Toolkit.getDefaultToolkit()
191
                        .getScreenResolution();
192

    
193
        /**
194
         * <p>
195
         * Area selected by user using some tool.
196
         * </p>
197
         * 
198
         * <p>
199
         * When the zoom changes (for instance when using the zoom in or zoom out
200
         * tools, but also zooming to a selected feature or shape) the extent that
201
         * covers that area is the value returned by this method. It is not the
202
         * actual area shown in the view because it does not care about the aspect
203
         * ratio of the available area. However, any part of the real world
204
         * contained in this extent is shown in the view.
205
         * </p>
206
         * <p>
207
         * Probably this is not what you are looking for. If you are looking for the
208
         * complete extent currently shown, you must use
209
         * {@linkplain #getAdjustedExtent()} method which returns the extent that
210
         * contains this one but regarding the current view's aspect ratio.
211
         * </p>
212
         * 
213
         * @see #getExtent()
214
         * @see #setEnvelope(Envelope)
215
         */
216
        protected Rectangle2D extent;
217

    
218
        /**
219
         * <p>
220
         * Location and dimensions of the extent adjusted to the image size.
221
         * </p>
222
         * 
223
         * @see #getAdjustedExtent()
224
         */
225
        protected Rectangle2D adjustedExtent;
226

    
227
        /**
228
         * Draw version of the context. It's used for know when de componend has
229
         * changed any visualization property
230
         * 
231
         * @see getDrawVersion
232
         * @see updateDrawVersion
233
         */
234
        private long drawVersion = 0L;
235

    
236
        /**
237
         * <p>
238
         * History with the last extents of the view.
239
         * </p>
240
         * 
241
         * @see #setPreviousExtent()
242
         * @see #getExtents()
243
         */
244
        protected ExtentHistory extents = new ExtentHistory();
245

    
246
        /**
247
         * <p>
248
         * Size in <i>screen coordinates</i> of the rectangle where the image is
249
         * displayed.
250
         * </p>
251
         * <p>
252
         * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
253
         * 
254
         * <ul>
255
         * <li>The new {@link #scale scale} .
256
         * <li>The new {@link #adjustedExtent adjustableExtent} .
257
         * <li>The new {@link #trans trans} .
258
         * <li>The new real world coordinates equivalent to 1 pixel (
259
         * {@link #dist1pixel dist1pixel}) .
260
         * <li>The new real world coordinates equivalent to 3 pixels (
261
         * {@link #dist3pixel dist3pixel}) .
262
         * </ul>
263
         * </p>
264
         * 
265
         * @see #getImageSize()
266
         * @see #getImageHeight()
267
         * @see #getImageWidth()
268
         * @see #setImageSize(Dimension)
269
         */
270
        private Dimension imageSize;
271

    
272
        /**
273
         * <p>
274
         * the affine transformation between the {@link #extent extent} in <i>map 2D
275
         * coordinates</i> to the image area in the screen, in <i>screen 2D
276
         * coordinates</i> (pixels).
277
         * </p>
278
         * 
279
         * @see AffineTransform
280
         * 
281
         * @see #getAffineTransform()
282
         * @see #setAffineTransform(AffineTransform)
283
         * @see #calculateAffineTransform()
284
         */
285
        private AffineTransform trans = new AffineTransform();
286

    
287
        /**
288
         * <p>
289
         * Measurement unit used for measuring distances and displaying information.
290
         * </p>
291
         * 
292
         * @see #getDistanceUnits()
293
         * @see #setDistanceUnits(int)
294
         */
295
        private int distanceUnits = 1;
296
        /**
297
         * <p>
298
         * Measurement unit used for measuring areas and displaying information.
299
         * </p>
300
         * 
301
         * @see #getDistanceArea()
302
         * @see #setDistanceArea(int)
303
         */
304
        private int distanceArea = 1;
305
        /**
306
         * <p>
307
         * Measurement unit used by this view port for the map.
308
         * </p>
309
         * 
310
         * @see #getMapUnits()
311
         * @see #setMapUnits(int)
312
         */
313
        private int mapUnits = 1;
314

    
315
        /**
316
         * <p>
317
         * Array with the {@link ViewPortListener ViewPortListener}s registered to
318
         * this view port.
319
         * </p>
320
         * 
321
         * @see #addViewPortListener(ViewPortListener)
322
         * @see #removeViewPortListener(ViewPortListener)
323
         */
324
        private ArrayList listeners = new ArrayList();
325

    
326
        /**
327
         * <p>
328
         * The offset is the position where start drawing the map.
329
         * </p>
330
         * <p>
331
         * The offset of a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
332
         * <i>View</i> is always (0, 0) because the drawing area fits with the full
333
         * window area. But in a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
334
         * <i>Layout</i> it's up to the place where the <code>FFrameView</code> is
335
         * located.
336
         * </p>
337
         * 
338
         * @see #getOffset()
339
         * @see #setOffset(Point2D)
340
         */
341
        private Point2D offset = new Point2D.Double(0, 0);
342

    
343
        /**
344
         * <p>
345
         * Clipping area.
346
         * </p>
347
         */
348
        // private Rectangle2D clip;
349

    
350
        /**
351
         * <p>
352
         * Background color of this view.
353
         * </p>
354
         * 
355
         * @see #getBackColor()
356
         * @see #setBackColor(Color)
357
         */
358
        private Color backColor = null; // Color.WHITE;
359

    
360
        /**
361
         * <p>
362
         * Information about the map projection used in this view.
363
         * </p>
364
         * 
365
         * @see #getProjection()
366
         * @see #setProjection(IProjection)
367
         */
368
        private IProjection proj;
369

    
370
        /**
371
         * <p>
372
         * Represents the distance in <i>world coordinates</i> equivalent to 1 pixel
373
         * in the view with the current extent.
374
         * </p>
375
         * 
376
         * @see #getDist1pixel()
377
         * @see #setDist1pixel(double)
378
         */
379
        private double dist1pixel;
380

    
381
        /**
382
         * <p>
383
         * Represents the distance in <i>world coordinates</i> equivalent to 3
384
         * pixels in the view with the current extent.
385
         * </p>
386
         * 
387
         * @see #getDist3pixel()
388
         * @see #setDist3pixel(double)
389
         */
390
        private double dist3pixel;
391

    
392
        /**
393
         * <p>
394
         * Ratio between the size of <code>imageSize</code> and <code>extent</code>:
395
         * <br>
396
         * <i>
397
         * 
398
         * <pre>
399
         * min{(imageSize.getHeight()/extent.getHeight(), imageSize.getWidth()/extent.getWidth())}
400
         * </pre>
401
         * 
402
         * </i>
403
         * </p>
404
         */
405
        private double scale;
406

    
407
        /**
408
         * <p>
409
         * Clipping area.
410
         * </p>
411
         * 
412
         * @see #setClipRect(Rectangle2D)
413
         */
414
        private Rectangle2D cliprect;
415

    
416
        /**
417
         * <p>
418
         * Enables or disables the <i>"adjustable extent"</i> mode.
419
         * </p>
420
         * 
421
         * <p>
422
         * When calculates the affine transform, if
423
         * <ul>
424
         * <li><i>enabled</i>: the new <code>adjustedExtent</code> will have the (X,
425
         * Y) coordinates of the <code>extent</code> and an area that will be an
426
         * scale of the image size. That area will have different height or width
427
         * (not both) of the extent according the least ratio (height or width) in
428
         * 
429
         * <pre>
430
         * image.size/extent.size&quot;
431
         * </pre>.
432
         * <li><i>disabled</i>: the new <code>adjustedExtent</code> will be like
433
         * <code>extent</code>.
434
         * </ul>
435
         * </p>
436
         * 
437
         * @see #setAdjustable(boolean)
438
         */
439
        private boolean adjustableExtent = true;
440

    
441
        public ViewPort() {
442
                
443
        }
444
        /**
445
         * <p>
446
         * Creates a new view port with the information of the projection in
447
         * <code>proj</code> argument, and default configuration:
448
         * </p>
449
         * <p>
450
         * <ul>
451
         * <li><i><code>distanceUnits</code></i> = meters
452
         * <li><i><code>mapUnits</code></i> = meters
453
         * <li><i><code>backColor</code></i> = <i>undefined</i>
454
         * <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
455
         * </ul>
456
         * </p>
457
         * 
458
         * @param proj
459
         *            information of the projection for this view port
460
         */
461
        public ViewPort(IProjection proj) {
462
                // Por defecto
463
                this.proj = proj;
464
        }
465

    
466
        /**
467
         * <p>
468
         * Changes the status of the <i>"adjustable extent"</i> option to enabled or
469
         * disabled.
470
         * </p>
471
         * 
472
         * <p>
473
         * If view port isn't adjustable, won't bear in mind the aspect ratio of the
474
         * available rectangular area to calculate the affine transform from the
475
         * original map in real coordinates. (Won't scale the image to adapt it to
476
         * the available rectangular area).
477
         * </p>
478
         * 
479
         * @param boolean the boolean to be set
480
         */
481
        public void setAdjustable(boolean adjustable) {
482
                if (adjustable == adjustableExtent) {
483
                        return;
484
                }
485
                adjustableExtent = adjustable;
486
                this.updateDrawVersion();
487
        }
488

    
489
        /**
490
         * <p>
491
         * Appends the specified {@link ViewPortListener ViewPortListener} listener
492
         * if weren't.
493
         * </p>
494
         * 
495
         * @param arg0
496
         *            the listener to add
497
         * 
498
         * @return <code>true</code> if has been added successfully
499
         * 
500
         * @see #removeViewPortListener(ViewPortListener)
501
         */
502
        public boolean addViewPortListener(ViewPortListener arg0) {
503
                if (!listeners.contains(arg0)) {
504
                        return listeners.add(arg0);
505
                }
506
                return false;
507
        }
508

    
509
        /**
510
         * <p>
511
         * Removes the specified {@link ViewPortListener ViewPortListener} listener,
512
         * if existed.
513
         * </p>
514
         * 
515
         * @param arg0
516
         *            the listener to remove
517
         * 
518
         * @return <code>true</code> if the contained the specified listener.
519
         * 
520
         * @see #addViewPortListener(ViewPortListener)
521
         */
522
        public boolean removeViewPortListener(ViewPortListener arg0) {
523
                return listeners.remove(arg0);
524
        }
525

    
526
        /**
527
         * <p>
528
         * Converts and returns the distance <code>d</code>, that is in <i>map
529
         * coordinates</i> to <i>screen coordinates</i> using a <i>delta
530
         * transform</i> with the transformation affine information in the
531
         * {@link #trans #trans} attribute.
532
         * </p>
533
         * 
534
         * @param d
535
         *            distance in <i>map coordinates</i>
536
         * 
537
         * @return distance equivalent in <i>screen coordinates</i>
538
         * 
539
         * @see #toMapDistance(int)
540
         * @see AffineTransform#deltaTransform(Point2D, Point2D)S
541
         */
542
        public int fromMapDistance(double d) {
543
                Point2D.Double pWorld = new Point2D.Double(1, 1);
544
                Point2D.Double pScreen = new Point2D.Double();
545

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

    
552
                return (int) (d * pScreen.x);
553
        }
554

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

    
576
                try {
577
                        trans.transform(pWorld, pScreen);
578
                } catch (Exception e) {
579
                        System.err.print(e.getMessage());
580
                }
581

    
582
                return pScreen;
583
        }
584

    
585
        /**
586
         * <p>
587
         * Converts and returns the 2D point argument, that is in <i>map
588
         * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
589
         * transformation in the {@link #trans #trans} attribute.
590
         * </p>
591
         * 
592
         * @param point
593
         *            the 2D point in <i>map coordinates</i>
594
         * 
595
         * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
596
         * 
597
         * @see #toMapPoint(Point2D)
598
         * @see #fromMapPoint(double, double)
599
         */
600
        public Point2D fromMapPoint(Point2D point) {
601
                return fromMapPoint(point.getX(), point.getY());
602
        }
603

    
604
        /**
605
         * <p>
606
         * Converts and returns the 2D point <code>(x,y)</code>, that is in
607
         * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using the
608
         * affine transformation in the {@link #trans #trans} attribute.
609
         * </p>
610
         * 
611
         * @param x
612
         *            the <code>x</code> <i>screen coordinate</i> of a 2D point
613
         * @param y
614
         *            the <code>y</code> <i>screen coordinate</i> of a 2D point
615
         * 
616
         * @return 2D point equivalent in <i>map coordinates</i>
617
         * 
618
         * @see #toMapPoint(Point2D)
619
         * @see #fromMapPoint(double, double)
620
         */
621
        public Point2D toMapPoint(int x, int y) {
622
                Point2D pScreen = new Point2D.Double(x, y);
623

    
624
                return toMapPoint(pScreen);
625
        }
626

    
627
        /**
628
         * <p>
629
         * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
630
         * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using
631
         * {@linkplain #toMapDistance(int)}, and {@linkplain #toMapPoint(int, int)}.
632
         * </p>
633
         * 
634
         * @param r
635
         *            the 2D rectangle in <i>screen coordinates</i> (pixels)
636
         * @return 2D rectangle equivalent in <i>map coordinates</i>
637
         * 
638
         * @see #fromMapRectangle(Rectangle2D)
639
         * @see #toMapDistance(int)
640
         * @see #toMapPoint(int, int)
641
         */
642
        public Rectangle2D toMapRectangle(Rectangle2D r) {
643
                Rectangle2D rect = new Rectangle2D.Double();
644
                Point2D p1 = toMapPoint((int) r.getX(), (int) r.getY());
645
                Point2D p2 = toMapPoint((int) r.getMaxX(), (int) r.getMaxY());
646
                rect.setFrameFromDiagonal(p1, p2);
647
                return rect;
648
        }
649

    
650
        /**
651
         * <p>
652
         * Converts and returns the distance <code>d</code>, that is in <i>screen
653
         * coordinates</i> to <i>map coordinates</i> using the transformation affine
654
         * information in the {@link #trans #trans} attribute.
655
         * </p>
656
         * 
657
         * @param d
658
         *            distance in pixels
659
         * 
660
         * @return distance equivalent in <i>map coordinates</i>
661
         * 
662
         * @see #fromMapDistance(double)
663
         * @see AffineTransform
664
         */
665
        public double toMapDistance(int d) {
666
                double dist = d / trans.getScaleX();
667

    
668
                return dist;
669
        }
670

    
671
        /**
672
         * <p>
673
         * Converts and returns the 2D point argument, that is in <i>screen
674
         * coordinates</i> (pixels) to <i>map coordinates</i> using the inverse
675
         * affine transformation of the {@link #trans #trans} attribute.
676
         * </p>
677
         * 
678
         * @param pScreen
679
         *            the 2D point in <i>screen coordinates</i> (pixels)
680
         * 
681
         * @return 2D point equivalent in <i>map coordinates</i>
682
         * 
683
         * @see #toMapPoint(int, int)
684
         * @see AffineTransform#createInverse()
685
         * @see AffineTransform#transform(Point2D, Point2D)
686
         */
687
        public Point2D toMapPoint(Point2D pScreen) {
688
                Point2D.Double pWorld = new Point2D.Double();
689
                AffineTransform at;
690

    
691
                try {
692
                        at = trans.createInverse();
693
                        at.transform(pScreen, pWorld);
694
                } catch (NoninvertibleTransformException e) {
695
                        throw new RuntimeException("Non invertible transform Exception", e);
696
                }
697

    
698
                return pWorld;
699
        }
700

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

    
724
                if ((proj != null) && !(proj instanceof UTM)) {
725
                        dist = new GeoCalc(proj).distanceVincenty(proj.toGeo(pt1), proj
726
                                        .toGeo(pt2));
727
                        return dist;
728
                }
729
                return (dist * MapContext.getDistanceTrans2Meter()[getMapUnits()]);
730
        }
731

    
732
        /**
733
         * <p>
734
         * Sets as extent and adjusted extent of this view port, the previous.
735
         * Recalculating its parameters.
736
         * </p>
737
         * 
738
         * @see #getExtents()
739
         * @see #calculateAffineTransform()
740
         * @deprecated use {@link ViewPort#setPreviousEnvelope()}
741
         */
742
        public void setPreviousExtent() {
743
                setPreviousEnvelope();
744
        }
745

    
746
        /**
747
         * <p>
748
         * Sets as envelope and adjusted envelope of this view port, the previous.
749
         * Recalculating its parameters.
750
         * </p>
751
         * 
752
         * @see #getExtents()
753
         * @see #calculateAffineTransform()
754
         */
755
        public void setPreviousEnvelope() {
756
                this.updateDrawVersion();
757
                extent = extents.removePrev();
758

    
759
                // Calcula la transformaci?n af?n
760
                calculateAffineTransform();
761

    
762
                // Lanzamos los eventos de extent cambiado
763
                callExtentChanged(getAdjustedExtent());
764
        }
765

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

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

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

    
887
                if (this.extent != null && this.extent.equals(newExtent)) {
888
                        return;
889
                }
890
                if (extent != null) {
891
                        extents.put(extent);
892
                }
893
                this.updateDrawVersion();
894
                this.extent = newExtent;
895

    
896
                // Calcula la transformaci?n af?n
897
                calculateAffineTransform();
898

    
899
                // Lanzamos los eventos de extent cambiado
900
                callExtentChanged(getAdjustedExtent());
901
        }
902

    
903
        /**
904
         * <p>
905
         * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
906
         * <ul>
907
         * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
908
         * scale, adjusted extent, affine transformation between map and screen
909
         * coordinates, the real world coordinates equivalent to 1 pixel, and the
910
         * real world coordinates equivalent to 3 pixels.
911
         * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered
912
         * that the extent has changed.
913
         * </ul>
914
         * </p>
915
         * 
916
         * @see #setEnvelope(Envelope)
917
         * @see #calculateAffineTransform()
918
         */
919
        public void refreshExtent() {
920
                // this.scale = scale;
921

    
922
                // Calcula la transformaci?n af?n
923
                calculateAffineTransform();
924

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

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

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

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

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

    
990
                if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
991
                        this.updateDrawVersion();
992
                        this.imageSize = imageSize;
993
                        calculateAffineTransform();
994
                }
995
        }
996

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

    
1015
                for (int i = 0; i < listeners.size(); i++) {
1016
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
1017
                        listener.extentChanged(ev);
1018
                }
1019
        }
1020

    
1021
        /**
1022
         * <p>
1023
         * Notifies to all view port listeners registered, that the background color
1024
         * of this view port has changed.
1025
         * </p>
1026
         * 
1027
         * @param c
1028
         *            the new background color
1029
         * 
1030
         * @see #setBackColor(Color)
1031
         * @see ColorEvent
1032
         * @see ViewPortListener
1033
         */
1034
        private void callColorChanged(Color c) {
1035
                ColorEvent ce = ColorEvent.createColorEvent(c);
1036

    
1037
                for (int i = 0; i < listeners.size(); i++) {
1038
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
1039
                        listener.backColorChanged(ce);
1040
                }
1041
        }
1042

    
1043
        /**
1044
         * <p>
1045
         * Notifies to all view port listeners registered, that the projection of
1046
         * this view port has changed.
1047
         * </p>
1048
         * 
1049
         * @param projection
1050
         *            the new projection
1051
         * 
1052
         * @see #setProjection(IProjection)
1053
         * @see ProjectionEvent
1054
         * @see ViewPortListener
1055
         */
1056
        private void callProjectionChanged(IProjection projection) {
1057
                ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
1058

    
1059
                for (int i = 0; i < listeners.size(); i++) {
1060
                        ViewPortListener listener = (ViewPortListener) listeners.get(i);
1061
                        listener.projectionChanged(ev);
1062
                }
1063
        }
1064

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

    
1101
                AffineTransform escalado = new AffineTransform();
1102
                AffineTransform translacion = new AffineTransform();
1103

    
1104
                double escalaX;
1105
                double escalaY;
1106

    
1107
                escalaX = imageSize.width / extent.getWidth();
1108
                escalaY = imageSize.height / extent.getHeight();
1109

    
1110
                double xCenter = extent.getCenterX();
1111
                double yCenter = extent.getCenterY();
1112
                double newHeight;
1113
                double newWidth;
1114

    
1115
                adjustedExtent = new Rectangle2D.Double();
1116

    
1117
                if (adjustableExtent) {
1118
                        if (escalaX < escalaY) {
1119
                                scale = escalaX;
1120
                                newHeight = imageSize.height / scale;
1121
                                adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0),
1122
                                                yCenter - (newHeight / 2.0), extent.getWidth(),
1123
                                                newHeight);
1124
                        } else {
1125
                                scale = escalaY;
1126
                                newWidth = imageSize.width / scale;
1127
                                adjustedExtent.setRect(xCenter - (newWidth / 2.0), yCenter
1128
                                                - (extent.getHeight() / 2.0), newWidth, extent
1129
                                                .getHeight());
1130
                        }
1131
                        escalado.setToScale(scale, -scale);
1132
                } else { // adjusted is same as extent
1133
                        scale = escalaX;
1134
                        adjustedExtent.setFrame(extent);
1135
                        escalado.setToScale(escalaX, -escalaY);
1136
                }
1137
                Envelope env = getAdjustedExtent();
1138
                if (env == null) {
1139
                        return;
1140
                }
1141
                translacion.setToTranslation(-env.getMinimum(0), -env.getMinimum(1)
1142
                                - getAdjustedExtent().getLength(1));
1143

    
1144
                AffineTransform offsetTrans = new AffineTransform();
1145
                offsetTrans.setToTranslation(offset.getX(), offset.getY());
1146

    
1147
                trans.setToIdentity();
1148
                trans.concatenate(offsetTrans);
1149
                trans.concatenate(escalado);
1150

    
1151
                trans.concatenate(translacion);
1152

    
1153
                // Calculamos las distancias de 1 pixel y 3 pixel con esa
1154
                // transformaci?n
1155
                // de coordenadas, de forma que est?n precalculadas para cuando las
1156
                // necesitemos
1157
                AffineTransform at;
1158

    
1159
                try {
1160
                        at = trans.createInverse();
1161

    
1162
                        Point2D pPixel = new Point2D.Float(1, 1);
1163
                        
1164
                        Point2D.Float pProv = new Point2D.Float();
1165
                        at.deltaTransform(pPixel, pProv);
1166

    
1167
                        dist1pixel = pProv.x;
1168
                        dist3pixel = 3 * pProv.x;
1169
                } catch (NoninvertibleTransformException e) {
1170
                        System.err.println("transformada afin = " + trans.toString());
1171
                        System.err.println("extent = " + extent.toString() + " imageSize= "
1172
                                        + imageSize.toString());
1173
                        throw new RuntimeException("Non invertible transform Exception", e);
1174
                }
1175
        }
1176

    
1177
        /**
1178
         * <p>
1179
         * Sets the offset.
1180
         * </p>
1181
         * <p>
1182
         * The offset is the position where start drawing the map.
1183
         * </p>
1184
         * 
1185
         * @param p
1186
         *            2D point that represents the offset in pixels
1187
         * 
1188
         * @see #getOffset()
1189
         */
1190
        public void setOffset(Point2D p) {
1191
                if (!offset.equals(p)) {
1192
                        this.updateDrawVersion();
1193
                        offset = p;
1194
                }
1195
        }
1196

    
1197
        /**
1198
         * <p>
1199
         * Gets the offset.
1200
         * </p>
1201
         * <p>
1202
         * The offset is the position where start drawing the map.
1203
         * </p>
1204
         * 
1205
         * @return 2D point that represents the offset in pixels
1206
         * 
1207
         * @see #setOffset(Point2D)
1208
         */
1209
        public Point2D getOffset() {
1210
                return offset;
1211
        }
1212

    
1213
        /**
1214
         * <p>
1215
         * Sets the background color.
1216
         * </p>
1217
         * 
1218
         * @param c
1219
         *            the new background color
1220
         * 
1221
         * @see #getBackColor()
1222
         */
1223
        public void setBackColor(Color c) {
1224
                if (!c.equals(this.backColor)) {
1225
                        this.updateDrawVersion();
1226
                        backColor = c;
1227
                        callColorChanged(backColor);
1228
                }
1229
        }
1230

    
1231
        /**
1232
         * <p>
1233
         * Gets the background color.
1234
         * </p>
1235
         * 
1236
         * @return the background color of the view
1237
         * 
1238
         * @see #setBackColor(Color)
1239
         */
1240
        public Color getBackColor() {
1241
                return backColor;
1242
        }
1243

    
1244
        /**
1245
         * <p>
1246
         * Returns the extent currently covered by the view adjusted (scaled) to the
1247
         * image size aspect.
1248
         * </p>
1249
         * 
1250
         * @return extent of the view adjusted to the image size aspect
1251
         * 
1252
         * @see #setAdjustable(boolean)
1253
         * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1254
         */
1255
        public Envelope getAdjustedExtent() {
1256
                return getAdjustedEnvelope();
1257
        }
1258

    
1259
        /**
1260
         * <p>
1261
         * Returns the envelope currently covered by the view adjusted (scaled) to
1262
         * the image size aspect.
1263
         * </p>
1264
         * 
1265
         * @return envelope of the view adjusted to the image size aspect
1266
         * 
1267
         * @see #setAdjustable(boolean)
1268
         */
1269
        public Envelope getAdjustedEnvelope() {
1270
                if (cliprect != null) {
1271
                        Rectangle2D r = adjustedExtent.createIntersection(cliprect);
1272
                        try {
1273
                                return geomManager.createEnvelope(r.getX(), r.getY(), r
1274
                                                .getMaxX(), r.getMaxY(), SUBTYPES.GEOM2D);
1275
                        } catch (CreateEnvelopeException e) {
1276
                                e.printStackTrace();
1277
                                logger.error("Error adjusting the extent", e);
1278
                        }
1279
                }
1280
                if (adjustedExtent != null) {
1281
                        try {
1282
                                return geomManager.createEnvelope(adjustedExtent.getX(),
1283
                                                adjustedExtent.getY(), adjustedExtent.getMaxX(),
1284
                                                adjustedExtent.getMaxY(), SUBTYPES.GEOM2D);
1285
                        } catch (CreateEnvelopeException e) {
1286
                                e.printStackTrace();
1287
                                logger.error("Error adjusting the extent", e);
1288
                        }
1289
                }
1290
                return null;
1291
        }
1292

    
1293
        /**
1294
         * <p>
1295
         * Returns the measurement unit of this view port used for measuring
1296
         * distances and displaying information.
1297
         * </p>
1298
         * 
1299
         * @return the measurement unit of this view used for measuring distances
1300
         *         and displaying information
1301
         * 
1302
         * @see #setDistanceUnits(int)
1303
         */
1304
        public int getDistanceUnits() {
1305
                return distanceUnits;
1306
        }
1307

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

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

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

    
1355
        /**
1356
         * <p>
1357
         * Gets the measurement unit used by this view port for the map.
1358
         * </p>
1359
         * 
1360
         * @return Returns the current map measure unit
1361
         * 
1362
         * @see #setMapUnits(int)
1363
         */
1364
        public int getMapUnits() {
1365
                return mapUnits;
1366
        }
1367

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

    
1382
        /**
1383
         * <p>
1384
         * Gets the width in <i>screen coordinates</i> of the rectangle where the
1385
         * image is displayed.
1386
         * </p>
1387
         * <p>
1388
         * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1389
         * 
1390
         * <ul>
1391
         * <li>The new {@link #scale scale} .
1392
         * <li>The new {@link #adjustedExtent adjustableExtent} .
1393
         * <li>The new {@link #trans trans} .
1394
         * <li>The new real world coordinates equivalent to 1 pixel (
1395
         * {@link #dist1pixel dist1pixel}) .
1396
         * <li>The new real world coordinates equivalent to 3 pixels (
1397
         * {@link #dist3pixel dist3pixel}) .
1398
         * </ul>
1399
         * </p>
1400
         * 
1401
         * @see #getImageHeight()
1402
         * @see #getImageSize()
1403
         * @see #setImageSize(Dimension)
1404
         */
1405
        public int getImageWidth() {
1406
                return imageSize.width;
1407
        }
1408

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

    
1436
        /**
1437
         * <p>
1438
         * Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1439
         * the view with the current extent.
1440
         * </p>
1441
         * 
1442
         * @return the distance
1443
         * 
1444
         * @see #setDist1pixel(double)
1445
         */
1446
        public double getDist1pixel() {
1447
                return dist1pixel;
1448
        }
1449

    
1450
        /**
1451
         * <p>
1452
         * Sets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1453
         * the view with the current extent.
1454
         * </p>
1455
         * 
1456
         * @param dist1pixel
1457
         *            the distance
1458
         * 
1459
         * @see #getDist1pixel()
1460
         */
1461
        public void setDist1pixel(double dist1pixel) {
1462
                if (dist1pixel == this.dist1pixel) {
1463
                        return;
1464
                }
1465
                this.updateDrawVersion();
1466
                this.dist1pixel = dist1pixel;
1467
        }
1468

    
1469
        /**
1470
         * <p>
1471
         * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1472
         * the view with the current extent.
1473
         * </p>
1474
         * 
1475
         * @return the distance
1476
         * 
1477
         * @see #setDist3pixel(double)
1478
         */
1479
        public double getDist3pixel() {
1480
                return dist3pixel;
1481
        }
1482

    
1483
        /**
1484
         * <p>
1485
         * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1486
         * the view with the current extent.
1487
         * </p>
1488
         * 
1489
         * @param dist3pixel
1490
         *            the distance
1491
         * 
1492
         * @see #getDist3pixel()
1493
         */
1494
        public void setDist3pixel(double dist3pixel) {
1495
                if (this.dist3pixel == dist3pixel) {
1496
                        return;
1497
                }
1498
                this.updateDrawVersion();
1499
                this.dist3pixel = dist3pixel;
1500
        }
1501

    
1502
        /**
1503
         * <p>
1504
         * Returns the last previous extents of this view port.
1505
         * </p>
1506
         * 
1507
         * @return the last previous extents of this view port
1508
         * 
1509
         * @see #setPreviousExtent()
1510
         * @deprecated use {@link ViewPort#getEnvelopes()}
1511
         */
1512
        public ExtentHistory getExtents() {
1513
                return getEnvelopes();
1514
        }
1515

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

    
1529
        /**
1530
         * <p>
1531
         * Gets the projection used in this view port.
1532
         * </p>
1533
         * 
1534
         * @return projection used in this view port
1535
         * 
1536
         * @see #setProjection(IProjection)
1537
         */
1538
        public IProjection getProjection() {
1539
                return proj;
1540
        }
1541

    
1542
        /**
1543
         * <p>
1544
         * Sets the projection to this view port.
1545
         * </p>
1546
         * 
1547
         * @param proj
1548
         *            the new projection
1549
         * 
1550
         * @see #getProjection()
1551
         */
1552
        public void setProjection(IProjection proj) {
1553
                if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1554
                        this.updateDrawVersion();
1555
                        this.proj = proj;
1556
                        callProjectionChanged(proj);
1557
                }
1558
        }
1559

    
1560
        // -----------------------------------------------------------------------------------------------------------
1561
        // NOTA PARA DESARROLLADORES SOBRE EL M?TODO
1562
        // "public void setAffineTransform(AffineTransform at)"
1563
        // ==============================================================================================
1564
        // Only used for print, should be removed, redefining the {@link
1565
        // RasterAdapter RasterAdapter} interface,
1566
        // allowing it to receive a {@link ViewPortData ViewPortData} .
1567
        // -----------------------------------------------------------------------------------------------------------
1568

    
1569
        /**
1570
         * <p>
1571
         * Sets only the affine transform to this view port, without updating
1572
         * dependent attributes.
1573
         * </p>
1574
         * <p>
1575
         * <b><i>This method could be problematic!</i></b>
1576
         * </p>
1577
         * 
1578
         * @param at
1579
         *            the affine transform to set
1580
         * 
1581
         * @see #getAffineTransform()
1582
         * @see #calculateAffineTransform()
1583
         */
1584
        public void setAffineTransform(AffineTransform at) {
1585
                this.trans = at;
1586
        }
1587

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

    
1650
                // XMLEntity xml = new XMLEntity();
1651
                // xml.putProperty("className", this.getClass().getName());
1652
                state.set("adjustedExtent", adjustedExtent);
1653
                
1654
                if (backColor != null) {
1655
                        state.set("backColor", color2String(backColor));
1656
                } else {
1657
                        throw new PersistenceException("Background color is null");
1658
                }
1659

    
1660
                if (cliprect != null) {
1661
                        state.set("clip", cliprect);
1662
                }
1663
                
1664
                state.set("dist1pixel", dist1pixel);
1665
                state.set("dist3pixel", dist3pixel);
1666
                state.set("distanceUnits", distanceUnits);
1667
                if (distanceArea != distanceUnits) {
1668
                        state.set("distanceArea", distanceArea);
1669
                }
1670
                
1671
                state.set("extent", extent);
1672
                state.set("extents", extents);
1673
                
1674
                state.set("mapUnits", mapUnits);
1675
                state.set("offsetX", offset.getX());
1676
                state.set("offsetY", offset.getY());
1677

    
1678
                state.set("proj", proj);
1679

    
1680
                state.set("scale", scale);
1681
        }
1682
        
1683
        
1684
        
1685
        public static void registerPersistent() {
1686
                
1687
                DynObjectManager dynMan = ToolsLocator.getDynObjectManager();
1688
                DynClass dynClass = dynMan.createDynClass(
1689
                "ViewPort_Persistent",
1690
                "ViewPort Persistent definition");
1691

    
1692
                DynField field;
1693
                
1694
        field = dynClass.addDynField("adjustedExtent");
1695
        field.setType(DataTypes.OBJECT);
1696
        field.setMandatory(true);
1697
        
1698
        field = dynClass.addDynField("backColor");
1699
        field.setType(DataTypes.STRING);
1700
        field.setMandatory(true);
1701
        
1702
        field = dynClass.addDynField("clip");
1703
        field.setType(DataTypes.OBJECT);
1704
        field.setMandatory(false);
1705
        
1706
        field = dynClass.addDynField("dist1pixel");
1707
        field.setType(DataTypes.DOUBLE);
1708
        field.setMandatory(true);
1709

    
1710
        field = dynClass.addDynField("dist3pixel");
1711
        field.setType(DataTypes.DOUBLE);
1712
        field.setMandatory(true);
1713

    
1714
        field = dynClass.addDynField("distanceUnits");
1715
        field.setType(DataTypes.INT);
1716
        field.setMandatory(true);
1717
        
1718
        field = dynClass.addDynField("distanceArea");
1719
        field.setType(DataTypes.INT);
1720
        field.setMandatory(false);
1721
        
1722
        field = dynClass.addDynField("extent");
1723
        field.setType(DataTypes.OBJECT);
1724
        field.setMandatory(true);
1725
        
1726
        field = dynClass.addDynField("extents");
1727
        field.setType(DataTypes.LIST);
1728
        field.setMandatory(true);
1729
        
1730
        field = dynClass.addDynField("mapUnits");
1731
        field.setType(DataTypes.INT);
1732
        field.setMandatory(true);
1733
        
1734
        field = dynClass.addDynField("offsetX");
1735
        field.setType(DataTypes.DOUBLE);
1736
        field.setMandatory(true);
1737

    
1738
        field = dynClass.addDynField("offsetY");
1739
        field.setType(DataTypes.DOUBLE);
1740
        field.setMandatory(true);
1741
        
1742
        field = dynClass.addDynField("proj");
1743
        field.setType(DataTypes.OBJECT);
1744
        field.setMandatory(true);
1745
        
1746
        field = dynClass.addDynField("scale");
1747
        field.setType(DataTypes.DOUBLE);
1748
        field.setMandatory(true);
1749
        
1750
                dynMan.add(dynClass);
1751

    
1752
                ToolsLocator.getPersistenceManager().registerClass(ViewPort.class, dynClass);                
1753
        }        
1754

    
1755
        public void loadFromState(PersistentState state) throws PersistenceException {
1756
                
1757
                // ViewPort vp = new ViewPort(null);
1758

    
1759
                adjustedExtent = (Rectangle2D) state.get("adjustedExtent");
1760
                
1761
                String str = state.getString("backColor");
1762
                setBackColor(string2Color(str));
1763

    
1764
                try {
1765
                        cliprect = (Rectangle2D) state.get("clip");
1766
                } catch (PersistenceException pex) {
1767
                        // not mandatory
1768
                        cliprect = null;
1769
                }
1770

    
1771
                setDist1pixel(state.getDouble("dist1pixel"));
1772
                setDist3pixel(state.getDouble("dist3pixel"));
1773
                setDistanceUnits(state.getInt("distanceUnits"));
1774

    
1775
                try {
1776
                        setDistanceArea(state.getInt("distanceArea"));
1777
                } catch (Exception pvnf) {
1778
                        // not mandatory , use dist units
1779
                        setDistanceArea(getDistanceUnits());
1780
                }        
1781
                
1782
                extents = (ExtentHistory) state.get("extents");
1783

    
1784
                extent = (Rectangle2D) state.get("extent");
1785

    
1786
                setMapUnits(state.getInt("mapUnits"));
1787

    
1788
                double x = state.getDouble("offsetX");
1789
                double y = state.getDouble("offsetY");
1790
                setOffset(new Point2D.Double(x, y));
1791

    
1792
                proj = (IProjection) state.get("proj");
1793

    
1794
                refreshExtent();
1795
        }
1796

    
1797
        /**
1798
         * <p>
1799
         * Fast clone implementation: creates and returns a clone of this view port
1800
         * using XML entities.
1801
         * </p>
1802
         * <p>
1803
         * Isn't a <i>deepclone</i> to avoid unnecessary memory consumption.
1804
         * </p>
1805
         * 
1806
         * @return the new view port
1807
         * 
1808
         * @see #createFromXML(XMLEntity)
1809
         */
1810
        public ViewPort cloneViewPort() {
1811
                return null; // createFromXML(getXMLEntity());
1812
        }
1813

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

    
1828
                String str;
1829
                str = "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1830
                                + adjustedExtent + "\nimageSize=" + imageSize + "\nescale="
1831
                                + scale + "\ntrans=" + trans;
1832

    
1833
                return str;
1834
        }
1835

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

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

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

    
1893
                double x1 = (-x * difw) - x + extent.getWidth() / 2;
1894
                double y1 = (-y * difw) - y + extent.getHeight() / 2;
1895
                double w1 = extent.getWidth() * difw;
1896
                double h1 = extent.getHeight() * difw;
1897
                extent.setRect(-x1, -y1, w1, h1);
1898
        }
1899

    
1900
        public long getDrawVersion() {
1901
                return this.drawVersion;
1902
        }
1903

    
1904
        protected void updateDrawVersion() {
1905
                this.drawVersion++;
1906
        }
1907
        
1908
        
1909
        
1910
        
1911
        
1912
    /**
1913
     * Obtiene la representaci?n de un color como String
1914
     *
1915
     * @param c Color
1916
     *
1917
     * @return String
1918
     */
1919
    public static String color2String(Color c) {
1920
            if (c == null) return null;
1921
        return c.getRed() + "," + c.getGreen() + "," + c.getBlue() + "," +
1922
        c.getAlpha();
1923
    }
1924

    
1925
    /**
1926
     * Obtiene el color de un string generado con color2String 
1927
     *
1928
     * @param stringColor string 
1929
     *
1930
     * @return Color
1931
     */
1932
    public static Color string2Color(String stringColor) {
1933
            if (stringColor == null || stringColor.equals("null")) return null;
1934
        String[] ints = new String[4];
1935
        
1936
        ints = CompatLocator.getStringUtils().split(stringColor, ",");
1937

    
1938
        int[] ret = new int[4];
1939

    
1940
        for (int i = 0; i < ret.length; i++) {
1941
            ret[i] = new Integer(ints[i]).intValue();
1942
        }
1943

    
1944
        return new Color(ret[0], ret[1], ret[2], ret[3]);
1945

    
1946
        /*
1947
           long color = new Long(stringColor).longValue();
1948
           long alpha = color / 16777216;
1949
           color = color % 16777216;
1950
        
1951
           long red = color / 65536;
1952
           color = color % 65536;
1953
           long green = color / 256;
1954
           color = color % 256;
1955
           long blue = color;
1956
           return new Color(red, green, blue, alpha);*/
1957
    }
1958

    
1959

    
1960

    
1961

    
1962
}