Statistics
| Revision:

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

History | View | Annotate | Download (58.8 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.color.ColorSpace;
47
import java.awt.geom.AffineTransform;
48
import java.awt.geom.NoninvertibleTransformException;
49
import java.awt.geom.Point2D;
50
import java.awt.geom.Rectangle2D;
51
import java.util.ArrayList;
52
import java.util.Iterator;
53

    
54
import org.cresques.cts.GeoCalc;
55
import org.cresques.cts.IProjection;
56
import org.cresques.cts.UTM;
57
import org.gvsig.compat.CompatLocator;
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.DynStruct;
69
import org.gvsig.tools.persistence.PersistenceManager;
70
import org.gvsig.tools.persistence.Persistent;
71
import org.gvsig.tools.persistence.PersistentState;
72
import org.gvsig.tools.persistence.exception.PersistenceException;
73
import org.slf4j.Logger;
74
import org.slf4j.LoggerFactory;
75

    
76
import org.gvsig.tools.lang.Cloneable;
77

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

    
126
    private static final String FIELD_DISTANCE_AREA = "distanceArea";
127
    private static final String FIELD_IMAGE_SIZE = "imageSize";
128
    private static final String FIELD_PROJ = "proj";
129
    private static final String FIELD_OFFSET = "offset";
130
    private static final String FIELD_MAP_UNITS = "mapUnits";
131
    private static final String FIELD_EXTENT = "extent";
132
    private static final String FIELD_EXTENTS = "extents";
133
    private static final String FIELD_DISTANCE_UNITS = "distanceUnits";
134
    private static final String FIELD_DIST3PIXEL = "dist3pixel";
135
    private static final String FIELD_DIST1PIXEL = "dist1pixel";
136
    private static final String FIELD_CLIP = "clip";
137
    private static final String FIELD_BACK_COLOR = "backColor";
138
    private static final String FIELD_ADJUSTED_EXTENT = "adjustedExtent";
139

    
140
    private static final GeometryManager geomManager =
141
        GeometryLocator.getGeometryManager();
142
    private static final Logger logger =
143
        LoggerFactory.getLogger(ViewPort.class);
144

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

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

    
182
    /**
183
     * <p>
184
     * Location and dimensions of the extent adjusted to the image size.
185
     * </p>
186
     * 
187
     * @see #getAdjustedExtent()
188
     */
189
    protected Rectangle2D adjustedExtent;
190

    
191
    /**
192
     * Draw version of the context. It's used for know when de componend has
193
     * changed any visualization property
194
     * 
195
     * @see getDrawVersion
196
     * @see updateDrawVersion
197
     */
198
    private long drawVersion = 0L;
199

    
200
    /**
201
     * <p>
202
     * History with the last extents of the view.
203
     * </p>
204
     * 
205
     * @see #setPreviousExtent()
206
     * @see #getExtents()
207
     */
208
    protected ExtentHistory extents = new ExtentHistory();
209

    
210
    /**
211
     * <p>
212
     * Size in <i>screen coordinates</i> of the rectangle where the image is
213
     * displayed.
214
     * </p>
215
     * <p>
216
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
217
     * 
218
     * <ul>
219
     * <li>The new {@link #scale scale} .
220
     * <li>The new {@link #adjustedExtent adjustableExtent} .
221
     * <li>The new {@link #trans trans} .
222
     * <li>The new real world coordinates equivalent to 1 pixel (
223
     * {@link #dist1pixel dist1pixel}) .
224
     * <li>The new real world coordinates equivalent to 3 pixels (
225
     * {@link #dist3pixel dist3pixel}) .
226
     * </ul>
227
     * </p>
228
     * 
229
     * @see #getImageSize()
230
     * @see #getImageHeight()
231
     * @see #getImageWidth()
232
     * @see #setImageSize(Dimension)
233
     */
234
    private Dimension imageSize;
235

    
236
    /**
237
     * <p>
238
     * the affine transformation between the {@link #extent extent} in <i>map 2D
239
     * coordinates</i> to the image area in the screen, in <i>screen 2D
240
     * coordinates</i> (pixels).
241
     * </p>
242
     * 
243
     * @see AffineTransform
244
     * 
245
     * @see #getAffineTransform()
246
     * @see #setAffineTransform(AffineTransform)
247
     * @see #calculateAffineTransform()
248
     */
249
    private AffineTransform trans = new AffineTransform();
250

    
251
    /**
252
     * <p>
253
     * Measurement unit used for measuring distances and displaying information.
254
     * </p>
255
     * 
256
     * @see #getDistanceUnits()
257
     * @see #setDistanceUnits(int)
258
     */
259
    private int distanceUnits = 1;
260
    /**
261
     * <p>
262
     * Measurement unit used for measuring areas and displaying information.
263
     * </p>
264
     * 
265
     * @see #getDistanceArea()
266
     * @see #setDistanceArea(int)
267
     */
268
    private int distanceArea = 1;
269
    /**
270
     * <p>
271
     * Measurement unit used by this view port for the map.
272
     * </p>
273
     * 
274
     * @see #getMapUnits()
275
     * @see #setMapUnits(int)
276
     */
277
    private int mapUnits = 1;
278

    
279
    /**
280
     * <p>
281
     * Array with the {@link ViewPortListener ViewPortListener}s registered to
282
     * this view port.
283
     * </p>
284
     * 
285
     * @see #addViewPortListener(ViewPortListener)
286
     * @see #removeViewPortListener(ViewPortListener)
287
     */
288
    private ArrayList listeners = new ArrayList();
289

    
290
    /**
291
     * <p>
292
     * The offset is the position where start drawing the map.
293
     * </p>
294
     * <p>
295
     * The offset of a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
296
     * <i>View</i> is always (0, 0) because the drawing area fits with the full
297
     * window area. But in a <a href="http://www.gvsig.gva.es/">gvSIG</a>'s
298
     * <i>Layout</i> it's up to the place where the <code>FFrameView</code> is
299
     * located.
300
     * </p>
301
     * 
302
     * @see #getOffset()
303
     * @see #setOffset(Point2D)
304
     */
305
    private Point2D offset = new Point2D.Double(0, 0);
306

    
307
    /**
308
     * <p>
309
     * Clipping area.
310
     * </p>
311
     */
312
    // private Rectangle2D clip;
313

    
314
    /**
315
     * <p>
316
     * Background color of this view.
317
     * </p>
318
     * 
319
     * @see #getBackColor()
320
     * @see #setBackColor(Color)
321
     */
322
    private Color backColor = null; // Color.WHITE;
323

    
324
    /**
325
     * <p>
326
     * Information about the map projection used in this view.
327
     * </p>
328
     * 
329
     * @see #getProjection()
330
     * @see #setProjection(IProjection)
331
     */
332
    private IProjection proj;
333

    
334
    /**
335
     * <p>
336
     * Represents the distance in <i>world coordinates</i> equivalent to 1 pixel
337
     * in the view with the current extent.
338
     * </p>
339
     * 
340
     * @see #getDist1pixel()
341
     * @see #setDist1pixel(double)
342
     */
343
    private double dist1pixel;
344

    
345
    /**
346
     * <p>
347
     * Represents the distance in <i>world coordinates</i> equivalent to 3
348
     * pixels in the view with the current extent.
349
     * </p>
350
     * 
351
     * @see #getDist3pixel()
352
     * @see #setDist3pixel(double)
353
     */
354
    private double dist3pixel;
355

    
356
    /**
357
     * <p>
358
     * Ratio between the size of <code>imageSize</code> and <code>extent</code>:
359
     * <br>
360
     * <i>
361
     * 
362
     * <pre>
363
     * min{(imageSize.getHeight()/extent.getHeight(), imageSize.getWidth()/extent.getWidth())}
364
     * </pre>
365
     * 
366
     * </i>
367
     * </p>
368
     */
369
    private double scale;
370

    
371
    /**
372
     * <p>
373
     * Clipping area.
374
     * </p>
375
     * 
376
     * @see #setClipRect(Rectangle2D)
377
     */
378
    private Rectangle2D cliprect;
379

    
380
    /**
381
     * <p>
382
     * Enables or disables the <i>"adjustable extent"</i> mode.
383
     * </p>
384
     * 
385
     * <p>
386
     * When calculates the affine transform, if
387
     * <ul>
388
     * <li><i>enabled</i>: the new <code>adjustedExtent</code> will have the (X,
389
     * Y) coordinates of the <code>extent</code> and an area that will be an
390
     * scale of the image size. That area will have different height or width
391
     * (not both) of the extent according the least ratio (height or width) in
392
     * 
393
     * <pre>
394
     * image.size/extent.size&quot;
395
     * </pre>.
396
     * <li><i>disabled</i>: the new <code>adjustedExtent</code> will be like
397
     * <code>extent</code>.
398
     * </ul>
399
     * </p>
400
     * 
401
     * @see #setAdjustable(boolean)
402
     */
403
    private boolean adjustableExtent = true;
404

    
405
    public ViewPort() {
406

    
407
    }
408

    
409
    /**
410
     * <p>
411
     * Creates a new view port with the information of the projection in
412
     * <code>proj</code> argument, and default configuration:
413
     * </p>
414
     * <p>
415
     * <ul>
416
     * <li><i><code>distanceUnits</code></i> = meters
417
     * <li><i><code>mapUnits</code></i> = meters
418
     * <li><i><code>backColor</code></i> = <i>undefined</i>
419
     * <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
420
     * </ul>
421
     * </p>
422
     * 
423
     * @param proj
424
     *            information of the projection for this view port
425
     */
426
    public ViewPort(IProjection proj) {
427
        // Por defecto
428
        this.proj = proj;
429
    }
430

    
431
    /**
432
     * <p>
433
     * Changes the status of the <i>"adjustable extent"</i> option to enabled or
434
     * disabled.
435
     * </p>
436
     * 
437
     * <p>
438
     * If view port isn't adjustable, won't bear in mind the aspect ratio of the
439
     * available rectangular area to calculate the affine transform from the
440
     * original map in real coordinates. (Won't scale the image to adapt it to
441
     * the available rectangular area).
442
     * </p>
443
     * 
444
     * @param boolean the boolean to be set
445
     */
446
    public void setAdjustable(boolean adjustable) {
447
        if (adjustable == adjustableExtent) {
448
            return;
449
        }
450
        adjustableExtent = adjustable;
451
        this.updateDrawVersion();
452
    }
453

    
454
    /**
455
     * <p>
456
     * Appends the specified {@link ViewPortListener ViewPortListener} listener
457
     * if weren't.
458
     * </p>
459
     * 
460
     * @param arg0
461
     *            the listener to add
462
     * 
463
     * @return <code>true</code> if has been added successfully
464
     * 
465
     * @see #removeViewPortListener(ViewPortListener)
466
     */
467
    public boolean addViewPortListener(ViewPortListener arg0) {
468
        if (!listeners.contains(arg0)) {
469
            return listeners.add(arg0);
470
        }
471
        return false;
472
    }
473

    
474
    /**
475
     * <p>
476
     * Removes the specified {@link ViewPortListener ViewPortListener} listener,
477
     * if existed.
478
     * </p>
479
     * 
480
     * @param arg0
481
     *            the listener to remove
482
     * 
483
     * @return <code>true</code> if the contained the specified listener.
484
     * 
485
     * @see #addViewPortListener(ViewPortListener)
486
     */
487
    public boolean removeViewPortListener(ViewPortListener arg0) {
488
        return listeners.remove(arg0);
489
    }
490

    
491
    /**
492
     * <p>
493
     * Converts and returns the distance <code>d</code>, that is in <i>map
494
     * coordinates</i> to <i>screen coordinates</i> using a <i>delta
495
     * transform</i> with the transformation affine information in the
496
     * {@link #trans #trans} attribute.
497
     * </p>
498
     * 
499
     * @param d
500
     *            distance in <i>map coordinates</i>
501
     * 
502
     * @return distance equivalent in <i>screen coordinates</i>
503
     * 
504
     * @see #toMapDistance(int)
505
     * @see AffineTransform#deltaTransform(Point2D, Point2D)S
506
     */
507
    public int fromMapDistance(double d) {
508
        Point2D.Double pWorld = new Point2D.Double(1, 1);
509
        Point2D.Double pScreen = new Point2D.Double();
510

    
511
        try {
512
            trans.deltaTransform(pWorld, pScreen);
513
        } catch (Exception e) {
514
            System.err.print(e.getMessage());
515
        }
516

    
517
        return (int) (d * pScreen.x);
518
    }
519

    
520
    /**
521
     * <p>
522
     * Converts and returns the 2D point <code>(x,y)</code>, that is in <i>map
523
     * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
524
     * transformation in the {@link #trans #trans} attribute.
525
     * </p>
526
     * 
527
     * @param x
528
     *            the <code>x</code> <i>map coordinate</i> of a 2D point
529
     * @param y
530
     *            the <code>y</code> <i>map coordinate</i> of a 2D point
531
     * 
532
     * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
533
     * 
534
     * @see #fromMapPoint(Point2D)
535
     * @see AffineTransform#transform(Point2D, Point2D)
536
     */
537
    public Point2D fromMapPoint(double x, double y) {
538
        Point2D.Double pWorld = new Point2D.Double(x, y);
539
        Point2D.Double pScreen = new Point2D.Double();
540

    
541
        try {
542
            trans.transform(pWorld, pScreen);
543
        } catch (Exception e) {
544
            System.err.print(e.getMessage());
545
        }
546

    
547
        return pScreen;
548
    }
549

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

    
569
    /**
570
     * <p>
571
     * Converts and returns the 2D point <code>(x,y)</code>, that is in
572
     * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using the
573
     * affine transformation in the {@link #trans #trans} attribute.
574
     * </p>
575
     * 
576
     * @param x
577
     *            the <code>x</code> <i>screen coordinate</i> of a 2D point
578
     * @param y
579
     *            the <code>y</code> <i>screen coordinate</i> of a 2D point
580
     * 
581
     * @return 2D point equivalent in <i>map coordinates</i>
582
     * 
583
     * @see #toMapPoint(Point2D)
584
     * @see #fromMapPoint(double, double)
585
     */
586
    public Point2D toMapPoint(int x, int y) {
587
        Point2D pScreen = new Point2D.Double(x, y);
588

    
589
        return toMapPoint(pScreen);
590
    }
591

    
592
    /**
593
     * <p>
594
     * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in
595
     * <i>screen coordinates</i> (pixels) to <i>map coordinates</i> using
596
     * {@linkplain #toMapDistance(int)}, and {@linkplain #toMapPoint(int, int)}.
597
     * </p>
598
     * 
599
     * @param r
600
     *            the 2D rectangle in <i>screen coordinates</i> (pixels)
601
     * @return 2D rectangle equivalent in <i>map coordinates</i>
602
     * 
603
     * @see #fromMapRectangle(Rectangle2D)
604
     * @see #toMapDistance(int)
605
     * @see #toMapPoint(int, int)
606
     */
607
    public Rectangle2D toMapRectangle(Rectangle2D r) {
608
        Rectangle2D rect = new Rectangle2D.Double();
609
        Point2D p1 = toMapPoint((int) r.getX(), (int) r.getY());
610
        Point2D p2 = toMapPoint((int) r.getMaxX(), (int) r.getMaxY());
611
        rect.setFrameFromDiagonal(p1, p2);
612
        return rect;
613
    }
614

    
615
    /**
616
     * <p>
617
     * Converts and returns the distance <code>d</code>, that is in <i>screen
618
     * coordinates</i> to <i>map coordinates</i> using the transformation affine
619
     * information in the {@link #trans #trans} attribute.
620
     * </p>
621
     * 
622
     * @param d
623
     *            distance in pixels
624
     * 
625
     * @return distance equivalent in <i>map coordinates</i>
626
     * 
627
     * @see #fromMapDistance(double)
628
     * @see AffineTransform
629
     */
630
    public double toMapDistance(int d) {
631
        double dist = d / trans.getScaleX();
632

    
633
        return dist;
634
    }
635

    
636
    /**
637
     * <p>
638
     * Converts and returns the 2D point argument, that is in <i>screen
639
     * coordinates</i> (pixels) to <i>map coordinates</i> using the inverse
640
     * affine transformation of the {@link #trans #trans} attribute.
641
     * </p>
642
     * 
643
     * @param pScreen
644
     *            the 2D point in <i>screen coordinates</i> (pixels)
645
     * 
646
     * @return 2D point equivalent in <i>map coordinates</i>
647
     * 
648
     * @see #toMapPoint(int, int)
649
     * @see AffineTransform#createInverse()
650
     * @see AffineTransform#transform(Point2D, Point2D)
651
     */
652
    public Point2D toMapPoint(Point2D pScreen) {
653
        Point2D.Double pWorld = new Point2D.Double();
654
        AffineTransform at;
655

    
656
        try {
657
            at = trans.createInverse();
658
            at.transform(pScreen, pWorld);
659
        } catch (NoninvertibleTransformException e) {
660
            throw new RuntimeException("Non invertible transform Exception", e);
661
        }
662

    
663
        return pWorld;
664
    }
665

    
666
    /**
667
     * <p>
668
     * Returns the real distance (in <i>world coordinates</i>) at the graphic
669
     * layers of two 2D points (in <i>map coordinates</i>) of the plane where is
670
     * selected the <i>extent</i>.
671
     * </p>
672
     * <p>
673
     * If the projection of this view is UTM, considers the Earth curvature.
674
     * </p>
675
     * 
676
     * @param pt1
677
     *            a 2D point in <i>map coordinates</i>
678
     * @param pt2
679
     *            another 2D point in <i>map coordinates</i>
680
     * 
681
     * @return the distance in meters between the two points 2D
682
     * 
683
     * @see GeoCalcImpl#distanceVincenty(Point2D, Point2D)
684
     */
685
    public double distanceWorld(Point2D pt1, Point2D pt2) {
686
        double dist = -1;
687
        dist = pt1.distance(pt2);
688

    
689
        if ((proj != null) && !(proj instanceof UTM)) {
690
            dist =
691
                new GeoCalc(proj).distanceVincenty(proj.toGeo(pt1),
692
                    proj.toGeo(pt2));
693
            return dist;
694
        }
695
        return (dist * MapContext.getDistanceTrans2Meter()[getMapUnits()]);
696
    }
697

    
698
    /**
699
     * <p>
700
     * Sets as extent and adjusted extent of this view port, the previous.
701
     * Recalculating its parameters.
702
     * </p>
703
     * 
704
     * @see #getExtents()
705
     * @see #calculateAffineTransform()
706
     * @deprecated use {@link ViewPort#setPreviousEnvelope()}
707
     */
708
    public void setPreviousExtent() {
709
        setPreviousEnvelope();
710
    }
711

    
712
    /**
713
     * <p>
714
     * Sets as envelope and adjusted envelope of this view port, the previous.
715
     * Recalculating its parameters.
716
     * </p>
717
     * 
718
     * @see #getExtents()
719
     * @see #calculateAffineTransform()
720
     */
721
    public void setPreviousEnvelope() {
722
        this.updateDrawVersion();
723
        extent = extents.removePrev();
724

    
725
        // Calcula la transformaci?n af?n
726
        calculateAffineTransform();
727

    
728
        // Lanzamos los eventos de extent cambiado
729
        callExtentChanged(getAdjustedExtent());
730
    }
731

    
732
    /**
733
     * <p>
734
     * Gets the area selected by user using some tool.
735
     * </p>
736
     * 
737
     * <p>
738
     * When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom
739
     * out</i> tools, but also zooming to a selected feature or shape) the
740
     * extent that covers that area is the value returned by this method. It is
741
     * not the actual area shown because it doesn't care about the aspect ratio
742
     * of the image size of the view. However, any part of the real world
743
     * contained in this extent is shown in the view.
744
     * </p>
745
     * 
746
     * <p>
747
     * If you are looking for the complete extent currently shown, you must use
748
     * the {@linkplain #getAdjustedExtent()} method.
749
     * </p>
750
     * 
751
     * @return the current extent
752
     * 
753
     * @see #setEnvelope(Envelope)
754
     * @see #getAdjustedExtent()
755
     * @see #setPreviousExtent()
756
     * @see #getExtents()
757
     * 
758
     * @deprecated use {@link ViewPort#getEnvelope()}
759
     */
760
    public Rectangle2D getExtent() {
761
        return extent;
762
    }
763

    
764
    /**
765
     * <p>
766
     * Gets the envelope selected by user using some tool.
767
     * </p>
768
     * 
769
     * <p>
770
     * When the zoom changes (for instance using the <i>zoom in</i> or <i>zoom
771
     * out</i> tools, but also zooming to a selected feature or shape) the
772
     * envelope that covers that area is the value returned by this method. It
773
     * is not the actual envelope shown because it doesn't care about the aspect
774
     * ratio of the image size of the view. However, any part of the real world
775
     * contained in this envelope is shown in the view.
776
     * </p>
777
     * 
778
     * <p>
779
     * If you are looking for the complete extent currently shown, you must use
780
     * the {@linkplain #getAdjustedEnvelope()} method.
781
     * </p>
782
     * 
783
     * @return the current envelope
784
     * 
785
     * @see #setEnvelope(Envelope)
786
     * @see #getAdjustedEnvelope()
787
     * @see #setPreviousEnvelope()
788
     * @see #getEnvelopes()
789
     */
790
    public Envelope getEnvelope() {
791
        if (this.extent == null) {
792
            return null;
793
        }
794
        try {
795
            return geomManager.createEnvelope(extent.getMinX(),
796
                extent.getMinY(),
797
                extent.getMaxX(),
798
                extent.getMaxY(),
799
                SUBTYPES.GEOM2D);
800
            // This class has to use Envelope instead of Rectangle2D. This catch
801
            // will disappear
802
        } catch (CreateEnvelopeException e) {
803
            logger.error("Error creating the envelope");
804
        }
805
        return null;
806
    }
807

    
808
    /**
809
     * <p>
810
     * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
811
     * <ul>
812
     * <li>Stores the previous extent.
813
     * <li>Calculates the new extent using <code>r</code>:
814
     * 
815
     * <pre>
816
     * extent =
817
     *     new Rectangle2D.Double(r.getMinX() - 0.1,
818
     *         r.getMinY() - 0.1,
819
     *         r.getWidth() + 0.2,
820
     *         r.getHeight() + 0.2);
821
     * </pre>
822
     * 
823
     * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
824
     * scale, adjusted extent, affine transformation between map and screen
825
     * coordinates, the real world coordinates equivalent to 1 pixel, and the
826
     * real world coordinates equivalent to 3 pixels.
827
     * <li>Notifies all {@link ViewPortListener ViewPortListener} registered
828
     * that the extent has changed.
829
     * </ul>
830
     * </p>
831
     * 
832
     * @param r
833
     *            the new extent
834
     * 
835
     * @see #getExtent()
836
     * @see #getExtents()
837
     * @see #calculateAffineTransform()
838
     * @see #setPreviousExtent()
839
     */
840
    public void setEnvelope(Envelope r) {
841
        Rectangle2D newExtent = null;
842
        // Esto comprueba que el extent no es de anchura o altura = "0"
843
        // y si es as? lo redimensiona.
844
        if (r != null) {
845
            if ((r.getMaximum(0) - r.getMinimum(0) == 0)
846
                || (r.getMaximum(1) - r.getMinimum(1) == 0)) {
847
                newExtent =
848
                    new Rectangle2D.Double(r.getMinimum(0) - 0.1,
849
                        r.getMinimum(1) - 0.1,
850
                        r.getMaximum(0) - r.getMinimum(0) + 0.2,
851
                        r.getMaximum(1) - r.getMinimum(1) + 0.2);
852
            } else {
853
                newExtent =
854
                    new Rectangle2D.Double(r.getMinimum(0),
855
                        r.getMinimum(1),
856
                        Math.abs(r.getMaximum(0) - r.getMinimum(0)),
857
                        Math.abs(r.getMaximum(1) - r.getMinimum(1)));
858
            }
859
        }
860

    
861
        if (this.extent != null && this.extent.equals(newExtent)) {
862
            return;
863
        }
864
        if (extent != null) {
865
            extents.put(extent);
866
        }
867
        this.updateDrawVersion();
868
        this.extent = newExtent;
869

    
870
        // Calcula la transformaci?n af?n
871
        calculateAffineTransform();
872

    
873
        // Lanzamos los eventos de extent cambiado
874
        callExtentChanged(getAdjustedExtent());
875
    }
876

    
877
    /**
878
     * <p>
879
     * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
880
     * <ul>
881
     * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
882
     * scale, adjusted extent, affine transformation between map and screen
883
     * coordinates, the real world coordinates equivalent to 1 pixel, and the
884
     * real world coordinates equivalent to 3 pixels.
885
     * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered
886
     * that the extent has changed.
887
     * </ul>
888
     * </p>
889
     * 
890
     * @see #setEnvelope(Envelope)
891
     * @see #calculateAffineTransform()
892
     */
893
    public void refreshExtent() {
894
        // this.scale = scale;
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
     * Calculates and returns using the current projection of this view port,
906
     * the scale that is the extent in <i>screen coordinates</i> from the image
907
     * in <i>map coordinates</i>.
908
     * </p>
909
     * 
910
     * @return the scale <i>extent / image size</i> projected by this view port
911
     * 
912
     * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
913
     */
914
    public double getScale() {
915
        return proj.getScale(extent.getMinX(),
916
            extent.getMaxX(),
917
            imageSize.width,
918
            dpi);
919
    }
920

    
921
    /**
922
     * <p>
923
     * Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D
924
     * coordinates</i> (pixels), preserving the "straightness" and "parallelism"
925
     * of the lines.
926
     * </p>
927
     * 
928
     * @return the affine transformation
929
     * 
930
     * @see #setAffineTransform(AffineTransform)
931
     * @see #calculateAffineTransform()
932
     */
933
    public AffineTransform getAffineTransform() {
934
        return trans;
935
    }
936

    
937
    /**
938
     * <p>
939
     * Returns the size of the image projected.
940
     * </p>
941
     * 
942
     * @return the image size
943
     * 
944
     * @see #setImageSize(Dimension)
945
     * @see #getImageHeight()
946
     * @see #getImageWidth()
947
     */
948
    public Dimension getImageSize() {
949
        return imageSize;
950
    }
951

    
952
    /**
953
     * <p>
954
     * Sets the size of the image projected, recalculating the parameters of
955
     * this view port.
956
     * </p>
957
     * 
958
     * @param imageSize
959
     *            the image size
960
     * 
961
     * @see #getImageSize()
962
     * @see #calculateAffineTransform()
963
     */
964
    public void setImageSize(Dimension imageSize) {
965

    
966
        if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
967
            this.updateDrawVersion();
968
            this.imageSize = imageSize;
969
            calculateAffineTransform();
970
        }
971
    }
972

    
973
    /**
974
     * <p>
975
     * Notifies to all view port listeners registered, that the adjusted extent
976
     * of this view port has changed.
977
     * </p>
978
     * 
979
     * @param newRect
980
     *            the new adjusted extend
981
     * 
982
     * @see #refreshExtent()
983
     * @see #setEnvelope(Envelope)
984
     * @see #setPreviousExtent()
985
     * @see ExtentEvent
986
     * @see ViewPortListener
987
     */
988
    protected void callExtentChanged(Envelope newRect) {
989
        ExtentEvent ev = ExtentEvent.createExtentEvent(newRect);
990

    
991
        for (int i = 0; i < listeners.size(); i++) {
992
            ViewPortListener listener = (ViewPortListener) listeners.get(i);
993
            listener.extentChanged(ev);
994
        }
995
    }
996

    
997
    /**
998
     * <p>
999
     * Notifies to all view port listeners registered, that the background color
1000
     * of this view port has changed.
1001
     * </p>
1002
     * 
1003
     * @param c
1004
     *            the new background color
1005
     * 
1006
     * @see #setBackColor(Color)
1007
     * @see ColorEvent
1008
     * @see ViewPortListener
1009
     */
1010
    private void callColorChanged(Color c) {
1011
        ColorEvent ce = ColorEvent.createColorEvent(c);
1012

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

    
1019
    /**
1020
     * <p>
1021
     * Notifies to all view port listeners registered, that the projection of
1022
     * this view port has changed.
1023
     * </p>
1024
     * 
1025
     * @param projection
1026
     *            the new projection
1027
     * 
1028
     * @see #setProjection(IProjection)
1029
     * @see ProjectionEvent
1030
     * @see ViewPortListener
1031
     */
1032
    private void callProjectionChanged(IProjection projection) {
1033
        ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
1034

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

    
1041
    /**
1042
     * <p>
1043
     * Calculates the affine transformation between the {@link #extent extent}
1044
     * in <i>map 2D coordinates</i> to the image area in the screen, in
1045
     * <i>screen 2D coordinates</i> (pixels).
1046
     * </p>
1047
     * 
1048
     * <p>
1049
     * This process recalculates some parameters of this view port:<br>
1050
     * 
1051
     * <ul>
1052
     * <li>The new {@link #scale scale} .
1053
     * <li>The new {@link #adjustedExtent adjustedExtent} .
1054
     * <li>The new {@link #trans trans} .
1055
     * <li>The new real world coordinates equivalent to 1 pixel (
1056
     * {@link #dist1pixel dist1pixel}) .
1057
     * <li>The new real world coordinates equivalent to 3 pixels (
1058
     * {@link #dist3pixel dist3pixel}) .
1059
     * </ul>
1060
     * </p>
1061
     * 
1062
     * @see #getAffineTransform()
1063
     * @see #setAffineTransform(AffineTransform)
1064
     * @see #refreshExtent()
1065
     * @see #setEnvelope(Envelope)
1066
     * @see #setImageSize(Dimension)
1067
     * @see #setPreviousExtent()
1068
     * @see #createFromXML(XMLEntity)
1069
     * @see AffineTransform
1070
     */
1071
    private void calculateAffineTransform() {
1072
        if ((imageSize == null) || (extent == null) || (imageSize.width <= 0)
1073
            || (imageSize.height <= 0)) {
1074
            return;
1075
        }
1076

    
1077
        AffineTransform escalado = new AffineTransform();
1078
        AffineTransform translacion = new AffineTransform();
1079

    
1080
        double escalaX;
1081
        double escalaY;
1082

    
1083
        escalaX = imageSize.width / extent.getWidth();
1084
        escalaY = imageSize.height / extent.getHeight();
1085

    
1086
        double xCenter = extent.getCenterX();
1087
        double yCenter = extent.getCenterY();
1088
        double newHeight;
1089
        double newWidth;
1090

    
1091
        adjustedExtent = new Rectangle2D.Double();
1092

    
1093
        if (adjustableExtent) {
1094
            if (escalaX < escalaY) {
1095
                scale = escalaX;
1096
                newHeight = imageSize.height / scale;
1097
                adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0),
1098
                    yCenter - (newHeight / 2.0),
1099
                    extent.getWidth(),
1100
                    newHeight);
1101
            } else {
1102
                scale = escalaY;
1103
                newWidth = imageSize.width / scale;
1104
                adjustedExtent.setRect(xCenter - (newWidth / 2.0), yCenter
1105
                    - (extent.getHeight() / 2.0), newWidth, extent.getHeight());
1106
            }
1107
            escalado.setToScale(scale, -scale);
1108
        } else { // adjusted is same as extent
1109
            scale = escalaX;
1110
            adjustedExtent.setFrame(extent);
1111
            escalado.setToScale(escalaX, -escalaY);
1112
        }
1113
        Envelope env = getAdjustedExtent();
1114
        if (env == null) {
1115
            return;
1116
        }
1117
        translacion.setToTranslation(-env.getMinimum(0), -env.getMinimum(1)
1118
            - getAdjustedExtent().getLength(1));
1119

    
1120
        AffineTransform offsetTrans = new AffineTransform();
1121
        offsetTrans.setToTranslation(offset.getX(), offset.getY());
1122

    
1123
        trans.setToIdentity();
1124
        trans.concatenate(offsetTrans);
1125
        trans.concatenate(escalado);
1126

    
1127
        trans.concatenate(translacion);
1128

    
1129
        // Calculamos las distancias de 1 pixel y 3 pixel con esa
1130
        // transformaci?n
1131
        // de coordenadas, de forma que est?n precalculadas para cuando las
1132
        // necesitemos
1133
        AffineTransform at;
1134

    
1135
        try {
1136
            at = trans.createInverse();
1137

    
1138
            Point2D pPixel = new Point2D.Float(1, 1);
1139

    
1140
            Point2D.Float pProv = new Point2D.Float();
1141
            at.deltaTransform(pPixel, pProv);
1142

    
1143
            dist1pixel = pProv.x;
1144
            dist3pixel = 3 * pProv.x;
1145
        } catch (NoninvertibleTransformException e) {
1146
            System.err.println("transformada afin = " + trans.toString());
1147
            System.err.println("extent = " + extent.toString() + " imageSize= "
1148
                + imageSize.toString());
1149
            throw new RuntimeException("Non invertible transform Exception", e);
1150
        }
1151
    }
1152

    
1153
    /**
1154
     * <p>
1155
     * Sets the offset.
1156
     * </p>
1157
     * <p>
1158
     * The offset is the position where start drawing the map.
1159
     * </p>
1160
     * 
1161
     * @param p
1162
     *            2D point that represents the offset in pixels
1163
     * 
1164
     * @see #getOffset()
1165
     */
1166
    public void setOffset(Point2D p) {
1167
        if (!offset.equals(p)) {
1168
            this.updateDrawVersion();
1169
            offset = p;
1170
        }
1171
    }
1172

    
1173
    /**
1174
     * <p>
1175
     * Gets the offset.
1176
     * </p>
1177
     * <p>
1178
     * The offset is the position where start drawing the map.
1179
     * </p>
1180
     * 
1181
     * @return 2D point that represents the offset in pixels
1182
     * 
1183
     * @see #setOffset(Point2D)
1184
     */
1185
    public Point2D getOffset() {
1186
        return offset;
1187
    }
1188

    
1189
    /**
1190
     * <p>
1191
     * Sets the background color.
1192
     * </p>
1193
     * 
1194
     * @param c
1195
     *            the new background color
1196
     * 
1197
     * @see #getBackColor()
1198
     */
1199
    public void setBackColor(Color c) {
1200
        if (!c.equals(this.backColor)) {
1201
            this.updateDrawVersion();
1202
            backColor = c;
1203
            callColorChanged(backColor);
1204
        }
1205
    }
1206

    
1207
    /**
1208
     * <p>
1209
     * Gets the background color.
1210
     * </p>
1211
     * 
1212
     * @return the background color of the view
1213
     * 
1214
     * @see #setBackColor(Color)
1215
     */
1216
    public Color getBackColor() {
1217
        return backColor;
1218
    }
1219

    
1220
    /**
1221
     * <p>
1222
     * Returns the extent currently covered by the view adjusted (scaled) to the
1223
     * image size aspect.
1224
     * </p>
1225
     * 
1226
     * @return extent of the view adjusted to the image size aspect
1227
     * 
1228
     * @see #setAdjustable(boolean)
1229
     * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1230
     */
1231
    public Envelope getAdjustedExtent() {
1232
        return getAdjustedEnvelope();
1233
    }
1234

    
1235
    /**
1236
     * <p>
1237
     * Returns the envelope currently covered by the view adjusted (scaled) to
1238
     * the image size aspect.
1239
     * </p>
1240
     * 
1241
     * @return envelope of the view adjusted to the image size aspect
1242
     * 
1243
     * @see #setAdjustable(boolean)
1244
     */
1245
    public Envelope getAdjustedEnvelope() {
1246
        if (cliprect != null) {
1247
            Rectangle2D r = adjustedExtent.createIntersection(cliprect);
1248
            try {
1249
                return geomManager.createEnvelope(r.getX(),
1250
                    r.getY(),
1251
                    r.getMaxX(),
1252
                    r.getMaxY(),
1253
                    SUBTYPES.GEOM2D);
1254
            } catch (CreateEnvelopeException e) {
1255
                e.printStackTrace();
1256
                logger.error("Error adjusting the extent", e);
1257
            }
1258
        }
1259
        if (adjustedExtent != null) {
1260
            try {
1261
                return geomManager.createEnvelope(adjustedExtent.getX(),
1262
                    adjustedExtent.getY(),
1263
                    adjustedExtent.getMaxX(),
1264
                    adjustedExtent.getMaxY(),
1265
                    SUBTYPES.GEOM2D);
1266
            } catch (CreateEnvelopeException e) {
1267
                e.printStackTrace();
1268
                logger.error("Error adjusting the extent", e);
1269
            }
1270
        }
1271
        return null;
1272
    }
1273

    
1274
    /**
1275
     * <p>
1276
     * Returns the measurement unit of this view port used for measuring
1277
     * distances and displaying information.
1278
     * </p>
1279
     * 
1280
     * @return the measurement unit of this view used for measuring distances
1281
     *         and displaying information
1282
     * 
1283
     * @see #setDistanceUnits(int)
1284
     */
1285
    public int getDistanceUnits() {
1286
        return distanceUnits;
1287
    }
1288

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

    
1304
    /**
1305
     * <p>
1306
     * Sets the measurement unit of this view port used for measuring distances
1307
     * and displaying information.
1308
     * </p>
1309
     * 
1310
     * @param distanceUnits
1311
     *            the measurement unit of this view used for measuring distances
1312
     *            and displaying information
1313
     * 
1314
     * @see #getDistanceUnits()
1315
     */
1316
    public void setDistanceUnits(int distanceUnits) {
1317
        this.distanceUnits = distanceUnits;
1318
    }
1319

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

    
1336
    /**
1337
     * <p>
1338
     * Gets the measurement unit used by this view port for the map.
1339
     * </p>
1340
     * 
1341
     * @return Returns the current map measure unit
1342
     * 
1343
     * @see #setMapUnits(int)
1344
     */
1345
    public int getMapUnits() {
1346
        return mapUnits;
1347
    }
1348

    
1349
    /**
1350
     * <p>
1351
     * Sets the measurement unit used by this view port for the map.
1352
     * </p>
1353
     * 
1354
     * @param mapUnits
1355
     *            the new map measure unit
1356
     * 
1357
     * @see #getMapUnits()
1358
     */
1359
    public void setMapUnits(int mapUnits) {
1360
        this.mapUnits = mapUnits;
1361
    }
1362

    
1363
    /**
1364
     * <p>
1365
     * Gets the width in <i>screen coordinates</i> of the rectangle where the
1366
     * image is displayed.
1367
     * </p>
1368
     * <p>
1369
     * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1370
     * 
1371
     * <ul>
1372
     * <li>The new {@link #scale scale} .
1373
     * <li>The new {@link #adjustedExtent adjustableExtent} .
1374
     * <li>The new {@link #trans trans} .
1375
     * <li>The new real world coordinates equivalent to 1 pixel (
1376
     * {@link #dist1pixel dist1pixel}) .
1377
     * <li>The new real world coordinates equivalent to 3 pixels (
1378
     * {@link #dist3pixel dist3pixel}) .
1379
     * </ul>
1380
     * </p>
1381
     * 
1382
     * @see #getImageHeight()
1383
     * @see #getImageSize()
1384
     * @see #setImageSize(Dimension)
1385
     */
1386
    public int getImageWidth() {
1387
        return imageSize.width;
1388
    }
1389

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

    
1417
    /**
1418
     * <p>
1419
     * Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1420
     * the view with the current extent.
1421
     * </p>
1422
     * 
1423
     * @return the distance
1424
     * 
1425
     * @see #setDist1pixel(double)
1426
     */
1427
    public double getDist1pixel() {
1428
        return dist1pixel;
1429
    }
1430

    
1431
    /**
1432
     * <p>
1433
     * Sets the distance in <i>world coordinates</i> equivalent to 1 pixel in
1434
     * the view with the current extent.
1435
     * </p>
1436
     * 
1437
     * @param dist1pixel
1438
     *            the distance
1439
     * 
1440
     * @see #getDist1pixel()
1441
     */
1442
    public void setDist1pixel(double dist1pixel) {
1443
        if (dist1pixel == this.dist1pixel) {
1444
            return;
1445
        }
1446
        this.updateDrawVersion();
1447
        this.dist1pixel = dist1pixel;
1448
    }
1449

    
1450
    /**
1451
     * <p>
1452
     * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1453
     * the view with the current extent.
1454
     * </p>
1455
     * 
1456
     * @return the distance
1457
     * 
1458
     * @see #setDist3pixel(double)
1459
     */
1460
    public double getDist3pixel() {
1461
        return dist3pixel;
1462
    }
1463

    
1464
    /**
1465
     * <p>
1466
     * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in
1467
     * the view with the current extent.
1468
     * </p>
1469
     * 
1470
     * @param dist3pixel
1471
     *            the distance
1472
     * 
1473
     * @see #getDist3pixel()
1474
     */
1475
    public void setDist3pixel(double dist3pixel) {
1476
        if (this.dist3pixel == dist3pixel) {
1477
            return;
1478
        }
1479
        this.updateDrawVersion();
1480
        this.dist3pixel = dist3pixel;
1481
    }
1482

    
1483
    /**
1484
     * <p>
1485
     * Returns the last previous extents of this view port.
1486
     * </p>
1487
     * 
1488
     * @return the last previous extents of this view port
1489
     * 
1490
     * @see #setPreviousExtent()
1491
     * @deprecated use {@link ViewPort#getEnvelopes()}
1492
     */
1493
    public ExtentHistory getExtents() {
1494
        return getEnvelopes();
1495
    }
1496

    
1497
    /**
1498
     * <p>
1499
     * Returns the last previous extents of this view port.
1500
     * </p>
1501
     * 
1502
     * @return the last previous extents of this view port
1503
     * 
1504
     * @see #setPreviousExtent()
1505
     */
1506
    public ExtentHistory getEnvelopes() {
1507
        return extents;
1508
    }
1509

    
1510
    /**
1511
     * <p>
1512
     * Gets the projection used in this view port.
1513
     * </p>
1514
     * 
1515
     * @return projection used in this view port
1516
     * 
1517
     * @see #setProjection(IProjection)
1518
     */
1519
    public IProjection getProjection() {
1520
        return proj;
1521
    }
1522

    
1523
    /**
1524
     * <p>
1525
     * Sets the projection to this view port.
1526
     * </p>
1527
     * 
1528
     * @param proj
1529
     *            the new projection
1530
     * 
1531
     * @see #getProjection()
1532
     */
1533
    public void setProjection(IProjection proj) {
1534
        if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1535
            this.updateDrawVersion();
1536
            this.proj = proj;
1537
            callProjectionChanged(proj);
1538
        }
1539
    }
1540

    
1541
    // -----------------------------------------------------------------------------------------------------------
1542
    // NOTA PARA DESARROLLADORES SOBRE EL M?TODO
1543
    // "public void setAffineTransform(AffineTransform at)"
1544
    // ==============================================================================================
1545
    // Only used for print, should be removed, redefining the {@link
1546
    // RasterAdapter RasterAdapter} interface,
1547
    // allowing it to receive a {@link ViewPortData ViewPortData} .
1548
    // -----------------------------------------------------------------------------------------------------------
1549

    
1550
    /**
1551
     * <p>
1552
     * Sets only the affine transform to this view port, without updating
1553
     * dependent attributes.
1554
     * </p>
1555
     * <p>
1556
     * <b><i>This method could be problematic!</i></b>
1557
     * </p>
1558
     * 
1559
     * @param at
1560
     *            the affine transform to set
1561
     * 
1562
     * @see #getAffineTransform()
1563
     * @see #calculateAffineTransform()
1564
     */
1565
    public void setAffineTransform(AffineTransform at) {
1566
        this.trans = at;
1567
    }
1568

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

    
1631
        state.set(FIELD_ADJUSTED_EXTENT, adjustedExtent);
1632
        state.set(FIELD_BACK_COLOR, backColor);
1633
        state.set(FIELD_CLIP, cliprect);
1634
        state.set(FIELD_DIST1PIXEL, dist1pixel);
1635
        state.set(FIELD_DIST3PIXEL, dist3pixel);
1636
        state.set(FIELD_DISTANCE_UNITS, distanceUnits);
1637
        state.set(FIELD_DISTANCE_AREA, distanceArea);
1638

    
1639
        state.set(FIELD_EXTENT, extent);
1640
        state.set(FIELD_EXTENTS, extents);
1641

    
1642
        state.set(FIELD_MAP_UNITS, mapUnits);
1643
        state.set(FIELD_OFFSET, offset);
1644

    
1645
        state.set(FIELD_PROJ, proj);
1646

    
1647
        state.set(FIELD_IMAGE_SIZE, imageSize);
1648
    }
1649

    
1650
    public static void registerPersistent() {
1651
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1652
        DynStruct definition =
1653
            manager.addDefinition(ViewPort.class,
1654
                "ViewPort",
1655
                "ViewPort Persistence definition",
1656
                null,
1657
                null);
1658

    
1659
        definition.addDynFieldObject(FIELD_ADJUSTED_EXTENT)
1660
            .setClassOfValue(Rectangle2D.class)
1661
            .setMandatory(false);
1662

    
1663
        definition.addDynFieldObject(FIELD_BACK_COLOR)
1664
            .setClassOfValue(Color.class)
1665
            .setMandatory(false);
1666

    
1667
        definition.addDynFieldObject(FIELD_CLIP)
1668
            .setClassOfValue(Rectangle2D.class)
1669
            .setMandatory(false);
1670

    
1671
        definition.addDynFieldDouble(FIELD_DIST1PIXEL).setMandatory(true);
1672

    
1673
        definition.addDynFieldDouble(FIELD_DIST3PIXEL).setMandatory(true);
1674

    
1675
        definition.addDynFieldInt(FIELD_DISTANCE_UNITS).setMandatory(true);
1676

    
1677
        definition.addDynFieldInt(FIELD_DISTANCE_AREA).setMandatory(false);
1678

    
1679
        definition.addDynFieldObject(FIELD_EXTENT)
1680
            .setClassOfValue(Rectangle2D.class)
1681
            .setMandatory(false);
1682

    
1683
        definition.addDynFieldObject(FIELD_EXTENTS)
1684
            .setClassOfValue(ExtentHistory.class)
1685
            .setMandatory(true);
1686

    
1687
        definition.addDynFieldInt(FIELD_MAP_UNITS).setMandatory(true);
1688

    
1689
        definition.addDynFieldDouble(FIELD_OFFSET)
1690
            .setClassOfValue(Point2D.class)
1691
            .setMandatory(false);
1692

    
1693
        definition.addDynFieldObject(FIELD_PROJ)
1694
            .setClassOfValue(IProjection.class)
1695
            .setMandatory(true);
1696

    
1697
        definition.addDynFieldInt(FIELD_IMAGE_SIZE)
1698
            .setClassOfValue(Dimension.class)
1699
            .setMandatory(false);
1700
    }
1701

    
1702
    public void loadFromState(PersistentState state) throws PersistenceException {
1703

    
1704
        adjustedExtent = (Rectangle2D) state.get(FIELD_ADJUSTED_EXTENT);
1705
        backColor = (Color) state.get(FIELD_BACK_COLOR);
1706
        cliprect = (Rectangle2D) state.get(FIELD_CLIP);
1707
        dist1pixel = state.getDouble(FIELD_DIST1PIXEL);
1708
        dist3pixel = state.getDouble(FIELD_DIST3PIXEL);
1709
        distanceUnits = state.getInt(FIELD_DISTANCE_UNITS);
1710
        extents = (ExtentHistory) state.get(FIELD_EXTENTS);
1711
        extent = (Rectangle2D) state.get(FIELD_EXTENT);
1712
        mapUnits = state.getInt(FIELD_MAP_UNITS);
1713
        offset = (Point2D) state.get(FIELD_OFFSET);
1714
        proj = (IProjection) state.get(FIELD_PROJ);
1715
        imageSize = (Dimension) state.get(FIELD_IMAGE_SIZE);
1716
        distanceArea = state.getInt(FIELD_DISTANCE_AREA);
1717

    
1718
        refreshExtent();
1719
    }
1720

    
1721
    /**
1722
     * Clone the view port without clone the listeners nor the extent history.
1723
     * 
1724
     * @return the cloned view port
1725
     */
1726
    public Object clone() throws CloneNotSupportedException {
1727

    
1728
        ViewPort clonedViewPort = (ViewPort) super.clone();
1729
        clonedViewPort.listeners = new ArrayList();
1730
        clonedViewPort.extents = new ExtentHistory();
1731

    
1732
        if (this.adjustedExtent!=null){
1733
            clonedViewPort.adjustedExtent =
1734
                (Rectangle2D) this.adjustedExtent.clone();
1735
        }
1736

    
1737
        if (this.cliprect!=null){
1738
            clonedViewPort.cliprect = (Rectangle2D) this.cliprect.clone();
1739
        }
1740
        
1741
        if (this.extent!=null) {
1742
            clonedViewPort.extent = (Rectangle2D) this.extent.clone();
1743
        }
1744
        if (this.imageSize!=null){
1745
            clonedViewPort.imageSize = (Dimension) this.imageSize.clone();
1746
        }
1747
        
1748
        if (this.offset!=null){
1749
            clonedViewPort.offset = (Point2D) this.offset.clone();
1750
        }
1751
        if (proj!=null){
1752
            clonedViewPort.proj = (IProjection) this.proj.clone();
1753
        }
1754
        
1755
        clonedViewPort.trans = (AffineTransform) this.trans.clone();
1756

    
1757
        return clonedViewPort;
1758
    }
1759

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

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

    
1780
        return str;
1781
    }
1782

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

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

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

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

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

    
1851
    protected void updateDrawVersion() {
1852
        this.drawVersion++;
1853
    }
1854

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

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

    
1883
        ints = CompatLocator.getStringUtils().split(stringColor, ",");
1884

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

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

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

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

    
1907
}