Statistics
| Revision:

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

History | View | Annotate | Download (55.3 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
package org.gvsig.fmap.mapcontext;
42

    
43
import java.awt.Color;
44
import java.awt.Dimension;
45
import java.awt.Toolkit;
46
import java.awt.geom.AffineTransform;
47
import java.awt.geom.NoninvertibleTransformException;
48
import java.awt.geom.Point2D;
49
import java.awt.geom.Rectangle2D;
50
import java.util.ArrayList;
51

    
52
import org.cresques.cts.GeoCalc;
53
import org.cresques.cts.IProjection;
54
import org.cresques.cts.UTM;
55
import org.gvsig.compat.CompatLocator;
56
import org.gvsig.fmap.crs.CRSFactory;
57
import org.gvsig.fmap.dal.DataTypes;
58
import org.gvsig.fmap.geom.GeometryLocator;
59
import org.gvsig.fmap.geom.GeometryManager;
60
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
61
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
62
import org.gvsig.fmap.geom.primitive.Envelope;
63
import org.gvsig.fmap.mapcontext.events.ColorEvent;
64
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
65
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
66
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
67
import org.gvsig.tools.ToolsLocator;
68
import org.gvsig.tools.dynobject.DynClass;
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
180
                        .getLogger(GeometryManager.class);
181

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
583
                return pScreen;
584
        }
585

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

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

    
625
                return toMapPoint(pScreen);
626
        }
627

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

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

    
669
                return dist;
670
        }
671

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

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

    
699
                return pWorld;
700
        }
701

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

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

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

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

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

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

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

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

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

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

    
894
                // Calcula la transformaci?n af?n
895
                calculateAffineTransform();
896

    
897
                // Lanzamos los eventos de extent cambiado
898
                callExtentChanged(getAdjustedExtent());
899
        }
900

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

    
920
                // Calcula la transformaci?n af?n
921
                calculateAffineTransform();
922

    
923
                // Lanzamos los eventos de extent cambiado
924
                callExtentChanged(getAdjustedExtent());
925
        }
926

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

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

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

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

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

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

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

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

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

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

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

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

    
1099
                AffineTransform escalado = new AffineTransform();
1100
                AffineTransform translacion = new AffineTransform();
1101

    
1102
                double escalaX;
1103
                double escalaY;
1104

    
1105
                escalaX = imageSize.width / extent.getWidth();
1106
                escalaY = imageSize.height / extent.getHeight();
1107

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

    
1113
                adjustedExtent = new Rectangle2D.Double();
1114

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

    
1142
                AffineTransform offsetTrans = new AffineTransform();
1143
                offsetTrans.setToTranslation(offset.getX(), offset.getY());
1144

    
1145
                trans.setToIdentity();
1146
                trans.concatenate(offsetTrans);
1147
                trans.concatenate(escalado);
1148

    
1149
                trans.concatenate(translacion);
1150

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

    
1157
                try {
1158
                        at = trans.createInverse();
1159

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1648
                // XMLEntity xml = new XMLEntity();
1649
                // xml.putProperty("className", this.getClass().getName());
1650

    
1651
                if (adjustedExtent != null) {
1652
                        state.set("adjustedExtentX", adjustedExtent.getX());
1653
                        state.set("adjustedExtentY", adjustedExtent.getY());
1654
                        state.set("adjustedExtentW", adjustedExtent.getWidth());
1655
                        state.set("adjustedExtentH", adjustedExtent.getHeight());
1656
                } else {
1657
                        throw new PersistenceException("Adjusted extent is null");
1658
                }
1659

    
1660
                if (backColor != null) {
1661
                        state.set("backColor", color2String(backColor));
1662
                } else {
1663
                        throw new PersistenceException("Background color is null");
1664
                }
1665

    
1666
                if (cliprect != null) {
1667
                        state.set("clipX", cliprect.getX());
1668
                        state.set("clipY", cliprect.getY());
1669
                        state.set("clipW", cliprect.getWidth());
1670
                        state.set("clipH", cliprect.getHeight());
1671
                } else {
1672
                        throw new PersistenceException("Clip area is null");
1673
                }
1674

    
1675
                state.set("dist1pixel", dist1pixel);
1676
                state.set("dist3pixel", dist3pixel);
1677
                state.set("distanceUnits", distanceUnits);
1678

    
1679
                if (extent != null) {
1680
                        state.set("extentX", extent.getX());
1681
                        state.set("extentY", extent.getY());
1682
                        state.set("extentW", extent.getWidth());
1683
                        state.set("extentH", extent.getHeight());
1684
                } else {
1685
                        throw new PersistenceException("Extent is null");
1686
                }
1687

    
1688
                state.set("extents", extents);
1689
                
1690
                state.set("mapUnits", mapUnits);
1691
                state.set("offsetX", offset.getX());
1692
                state.set("offsetY", offset.getY());
1693

    
1694
                if (proj != null) {
1695
                        state.set("proj", proj.getAbrev());
1696
                } else {
1697
                        // throw new PersistenceException("Projection is null");
1698
                }
1699

    
1700
                state.set("scale", scale);
1701
        }
1702
        
1703
        
1704
        
1705
        public static void registerPersistent() {
1706
                
1707
                DynObjectManager dynMan = ToolsLocator.getDynObjectManager();
1708
                DynClass dynClass = dynMan.createDynClass(
1709
                "ViewPort_Persistent",
1710
                "ViewPort Persistent definition");
1711

    
1712
                dynClass.addDynFieldSingle("adjustedExtentX", DataTypes.DOUBLE, null, true, true);
1713
                dynClass.addDynFieldSingle("adjustedExtentY", DataTypes.DOUBLE, null, true, true);
1714
                dynClass.addDynFieldSingle("adjustedExtentW", DataTypes.DOUBLE, null, true, true);
1715
                dynClass.addDynFieldSingle("adjustedExtentH", DataTypes.DOUBLE, null, true, true);
1716
                
1717
                dynClass.addDynFieldSingle("backColor", DataTypes.STRING, null, true, true);
1718
                
1719
                dynClass.addDynFieldSingle("clipX", DataTypes.DOUBLE, null, true, true);
1720
                dynClass.addDynFieldSingle("clipY", DataTypes.DOUBLE, null, true, true);
1721
                dynClass.addDynFieldSingle("clipW", DataTypes.DOUBLE, null, true, true);
1722
                dynClass.addDynFieldSingle("clipH", DataTypes.DOUBLE, null, true, true);
1723
                
1724
                dynClass.addDynFieldSingle("dist1pixel", DataTypes.DOUBLE, null, true, true);
1725
                dynClass.addDynFieldSingle("dist3pixel", DataTypes.DOUBLE, null, true, true);
1726
                dynClass.addDynFieldSingle("distanceUnits", DataTypes.INT, null, true, true);
1727
                
1728
                dynClass.addDynFieldSingle("extentX", DataTypes.DOUBLE, null, true, true);
1729
                dynClass.addDynFieldSingle("extentY", DataTypes.DOUBLE, null, true, true);
1730
                dynClass.addDynFieldSingle("extentW", DataTypes.DOUBLE, null, true, true);
1731
                dynClass.addDynFieldSingle("extentH", DataTypes.DOUBLE, null, true, true);
1732
                dynClass.addDynFieldSingle("extents", DataTypes.PERSISTENT, null, true, true);
1733
                
1734
                dynClass.addDynFieldSingle("mapUnits", DataTypes.INT, null, true, true);
1735
                dynClass.addDynFieldSingle("offsetX", DataTypes.DOUBLE, null, true, true);
1736
                dynClass.addDynFieldSingle("offsetY", DataTypes.DOUBLE, null, true, true);
1737
                
1738
                dynClass.addDynFieldSingle("proj", DataTypes.STRING, null, true, true);
1739
                dynClass.addDynFieldSingle("scale", DataTypes.DOUBLE, null, true, true);
1740
                dynMan.add(dynClass);
1741

    
1742
                ToolsLocator.getPersistenceManager().registerClass(ViewPort.class, dynClass);                
1743
        }        
1744

    
1745
        public void loadFromState(PersistentState state) throws PersistenceException {
1746
                
1747
                // ViewPort vp = new ViewPort(null);
1748

    
1749
                double x = 0, y = 0, w = 0, h = 0;
1750

    
1751
                boolean exist = false;
1752
                try {
1753
                        x = state.getDouble("adjustedExtentX");
1754
                        exist = true;
1755
                } catch (PersistenceValueNotFoundException pvnf) { }
1756

    
1757
                if (exist) {
1758
                        adjustedExtent = new Rectangle2D.Double(
1759
                                        x,
1760
                                        state.getDouble("adjustedExtentY"),
1761
                                        state.getDouble("adjustedExtentW"),
1762
                                        state.getDouble("adjustedExtentH"));
1763
                }
1764
                        
1765
                String str;
1766

    
1767
                try {
1768
                        str = state.getString("backColor");
1769
                        setBackColor(string2Color(str));                        
1770

    
1771
                } catch (PersistenceValueNotFoundException pvnf) {
1772
                        setBackColor(Color.white);
1773
                }
1774
                
1775
                exist = false;
1776
                try {
1777
                        x = state.getDouble("clipX");
1778
                        exist = true;
1779
                } catch (PersistenceValueNotFoundException pvnf) { }                
1780

    
1781
                if (exist) {
1782
                        cliprect = new Rectangle2D.Double(
1783
                                        x,
1784
                                        state.getDouble("clipY"),
1785
                                        state.getDouble("clipW"),
1786
                                        state.getDouble("clipH"));
1787
                }
1788

    
1789
                setDist1pixel(state.getDouble("dist1pixel"));
1790
                setDist3pixel(state.getDouble("dist3pixel"));
1791
                setDistanceUnits(state.getInt("distanceUnits"));
1792
                
1793
                exist = false;
1794
                int d = 0;
1795
                try {
1796
                        d = state.getInt("distanceArea");
1797
                        exist = true;
1798
                } catch (PersistenceValueNotFoundException pvnf) { }        
1799
                
1800
                if (exist) {
1801
                        setDistanceArea(d);
1802
                } else {
1803
                        setDistanceArea(state.getInt("distanceUnits"));
1804
                }
1805
                
1806
                extents = (ExtentHistory) state.get("extents");
1807

    
1808
                exist = false;
1809
                
1810
                try {
1811
                        x = state.getDouble("extentX");
1812
                        exist = true;
1813
                } catch (PersistenceValueNotFoundException exc) { }
1814
                
1815
                if (exist) {
1816
                        y = state.getDouble("extentY");
1817
                        w = state.getDouble("extentW");
1818
                        h = state.getDouble("extentH");
1819
                        try {
1820
                                setEnvelope(geomManager.createEnvelope(x, y, x+w, y+h, SUBTYPES.GEOM2D));
1821
                        } catch (CreateEnvelopeException e) {
1822
                                e.printStackTrace();
1823
                                logger.error("Error setting the extent", e);
1824
                        }
1825
                }
1826

    
1827
                setMapUnits(state.getInt("mapUnits"));
1828
                x = state.getDouble("offsetX");
1829
                y = state.getDouble("offsetY");
1830
                setOffset(new Point2D.Double(x, y));
1831

    
1832
                try {
1833
                        proj = CRSFactory.getCRS(state.getString("proj"));
1834
                } catch (PersistenceValueNotFoundException exc) { }
1835

    
1836
                refreshExtent();
1837
        }
1838

    
1839
        /**
1840
         * <p>
1841
         * Fast clone implementation: creates and returns a clone of this view port
1842
         * using XML entities.
1843
         * </p>
1844
         * <p>
1845
         * Isn't a <i>deepclone</i> to avoid unnecessary memory consumption.
1846
         * </p>
1847
         * 
1848
         * @return the new view port
1849
         * 
1850
         * @see #createFromXML(XMLEntity)
1851
         */
1852
        public ViewPort cloneViewPort() {
1853
                return null; // createFromXML(getXMLEntity());
1854
        }
1855

    
1856
        /**
1857
         * <p>
1858
         * Returns a <code>String</code> representation of the main values of this
1859
         * view port: <code>{@linkplain #extent}</code>,
1860
         * <code>{@linkplain #adjustedExtent}</code>,
1861
         * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>,
1862
         * and <code>{@linkplain #trans}</code>.
1863
         * </p>
1864
         * 
1865
         * @return a <code>string</code> representation of the main values of this
1866
         *         view port
1867
         */
1868
        public String toString() {
1869

    
1870
                String str;
1871
                str = "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1872
                                + adjustedExtent + "\nimageSize=" + imageSize + "\nescale="
1873
                                + scale + "\ntrans=" + trans;
1874

    
1875
                return str;
1876
        }
1877

    
1878
        /**
1879
         * <p>
1880
         * Sets the position and size of the clipping rectangle.
1881
         * </p>
1882
         * 
1883
         * @param rectView
1884
         *            the clipping rectangle to set
1885
         */
1886
        public void setClipRect(Rectangle2D rectView) {
1887
                this.updateDrawVersion();
1888
                cliprect = rectView;
1889
        }
1890

    
1891
        /**
1892
         * <p>
1893
         * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
1894
         * <i>map coordinates</i> to <i>screen coordinates</i> (pixels) using an
1895
         * <i>inverse transform</i> with the transformation affine information in
1896
         * the {@link #trans #trans} attribute.
1897
         * </p>
1898
         * 
1899
         * @param r
1900
         *            the 2D rectangle in <i>map coordinates</i>
1901
         * @return 2D rectangle equivalent in <i>screen coordinates</i> (pixels)
1902
         * 
1903
         * @see #toMapRectangle(Rectangle2D)
1904
         * @see #fromMapDistance(double)
1905
         * @see #fromMapPoint(Point2D)
1906
         */
1907
        public Rectangle2D fromMapRectangle(Rectangle2D r) {
1908
                Rectangle2D rect = new Rectangle2D.Double();
1909
                Point2D p1 = fromMapPoint((int) r.getX(), (int) r.getY());
1910
                Point2D p2 = fromMapPoint((int) r.getMaxX(), (int) r.getMaxY());
1911
                rect.setFrameFromDiagonal(p1, p2);
1912
                return rect;
1913
        }
1914

    
1915
        /**
1916
         * <p>
1917
         * Recalculates the current <code>{@linkplain #extent}</code> using an
1918
         * scale. It's necessary execute {@linkplain #refreshExtent()} after.
1919
         * </p>
1920
         * 
1921
         * @param s
1922
         *            the scale to set
1923
         * 
1924
         * @deprecated since 07/09/07, use
1925
         *             {@linkplain MapContext#setScaleView(long)}
1926
         */
1927
        public void setScale(long s) {
1928
                double x = extent.getX();
1929
                double y = extent.getY();
1930
                double escalaX = imageSize.width / extent.getWidth();
1931
                double w = imageSize.width / s;
1932
                double h = imageSize.height / s;
1933
                double difw = escalaX / s;
1934

    
1935
                double x1 = (-x * difw) - x + extent.getWidth() / 2;
1936
                double y1 = (-y * difw) - y + extent.getHeight() / 2;
1937
                double w1 = extent.getWidth() * difw;
1938
                double h1 = extent.getHeight() * difw;
1939
                extent.setRect(-x1, -y1, w1, h1);
1940
        }
1941

    
1942
        public long getDrawVersion() {
1943
                return this.drawVersion;
1944
        }
1945

    
1946
        protected void updateDrawVersion() {
1947
                this.drawVersion++;
1948
        }
1949
        
1950
        
1951
        
1952
        
1953
        
1954
    /**
1955
     * Obtiene la representaci?n de un color como String
1956
     *
1957
     * @param c Color
1958
     *
1959
     * @return String
1960
     */
1961
    public static String color2String(Color c) {
1962
            if (c == null) return null;
1963
        return c.getRed() + "," + c.getGreen() + "," + c.getBlue() + "," +
1964
        c.getAlpha();
1965
    }
1966

    
1967
    /**
1968
     * Obtiene el color de un string generado con color2String 
1969
     *
1970
     * @param stringColor string 
1971
     *
1972
     * @return Color
1973
     */
1974
    public static Color string2Color(String stringColor) {
1975
            if (stringColor == null || stringColor.equals("null")) return null;
1976
        String[] ints = new String[4];
1977
        
1978
        ints = CompatLocator.getStringUtils().split(stringColor, ",");
1979

    
1980
        int[] ret = new int[4];
1981

    
1982
        for (int i = 0; i < ret.length; i++) {
1983
            ret[i] = new Integer(ints[i]).intValue();
1984
        }
1985

    
1986
        return new Color(ret[0], ret[1], ret[2], ret[3]);
1987

    
1988
        /*
1989
           long color = new Long(stringColor).longValue();
1990
           long alpha = color / 16777216;
1991
           color = color % 16777216;
1992
        
1993
           long red = color / 65536;
1994
           color = color % 65536;
1995
           long green = color / 256;
1996
           color = color % 256;
1997
           long blue = color;
1998
           return new Color(red, green, blue, alpha);*/
1999
    }
2000

    
2001

    
2002

    
2003

    
2004
}