Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.mapcontext / org.gvsig.fmap.mapcontext.api / src / main / java / org / gvsig / fmap / mapcontext / ViewPort.java @ 42177

History | View | Annotate | Download (55.1 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.mapcontext;
25

    
26
import java.awt.Color;
27
import java.awt.Dimension;
28
import java.awt.Toolkit;
29
import java.awt.geom.AffineTransform;
30
import java.awt.geom.NoninvertibleTransformException;
31
import java.awt.geom.Point2D;
32
import java.awt.geom.Rectangle2D;
33
import java.util.ArrayList;
34

    
35
import org.cresques.cts.GeoCalc;
36
import org.cresques.cts.IProjection;
37
import org.gvsig.compat.CompatLocator;
38
import org.slf4j.Logger;
39
import org.slf4j.LoggerFactory;
40

    
41
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
42
import org.gvsig.fmap.geom.Geometry;
43
import org.gvsig.fmap.geom.GeometryLocator;
44
import org.gvsig.fmap.geom.GeometryManager;
45
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
46
import org.gvsig.fmap.geom.exception.CreateGeometryException;
47
import org.gvsig.fmap.geom.primitive.Envelope;
48
import org.gvsig.fmap.geom.primitive.Point;
49
import org.gvsig.fmap.mapcontext.events.ColorEvent;
50
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
51
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
52
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
53
import org.gvsig.timesupport.Time;
54
import org.gvsig.tools.ToolsLocator;
55
import org.gvsig.tools.dynobject.DynStruct;
56
import org.gvsig.tools.lang.Cloneable;
57
import org.gvsig.tools.persistence.PersistenceManager;
58
import org.gvsig.tools.persistence.Persistent;
59
import org.gvsig.tools.persistence.PersistentState;
60
import org.gvsig.tools.persistence.exception.PersistenceException;
61
import org.gvsig.tools.util.Callable;
62

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

    
109
  private static final String FIELD_DISTANCE_AREA = "distanceArea";
110

    
111
  private static final String FIELD_IMAGE_SIZE = "imageSize";
112

    
113
  private static final String FIELD_PROJ = "proj";
114

    
115
  private static final String FIELD_OFFSET = "offset";
116

    
117
  private static final String FIELD_MAP_UNITS = "mapUnits";
118

    
119
  private static final String FIELD_EXTENT = "extent";
120

    
121
  private static final String FIELD_EXTENTS = "extents";
122

    
123
  private static final String FIELD_DISTANCE_UNITS = "distanceUnits";
124

    
125
  private static final String FIELD_DIST3PIXEL = "dist3pixel";
126

    
127
  private static final String FIELD_DIST1PIXEL = "dist1pixel";
128

    
129
  private static final String FIELD_CLIP = "clip";
130

    
131
  private static final String FIELD_BACK_COLOR = "backColor";
132

    
133
  private static final String FIELD_ADJUSTED_EXTENT = "adjustedExtent";
134

    
135
  private static final GeometryManager geomManager = GeometryLocator
136
      .getGeometryManager();
137

    
138
  private static final Logger logger = LoggerFactory.getLogger(ViewPort.class);
139

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

    
164
  protected Time time;
165

    
166
  /**
167
   * <p>
168
   * Location and dimensions of the extent adjusted to the image size.
169
   * </p>
170
   * 
171
   * @see #getAdjustedExtent()
172
   */
173
  protected Rectangle2D adjustedExtent;
174

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

    
184
  /**
185
   * <p>
186
   * History with the last extents of the view.
187
   * </p>
188
   * 
189
   * @see #setPreviousExtent()
190
   * @see #getExtents()
191
   */
192
  protected ExtentHistory extentsHistory = new ExtentHistory();
193

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

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

    
233
  /**
234
   * <p>
235
   * Measurement unit used for measuring distances and displaying information.
236
   * </p>
237
   * 
238
   * @see #getDistanceUnits()
239
   * @see #setDistanceUnits(int)
240
   */
241
  private int distanceUnits = 1;
242

    
243
  /**
244
   * <p>
245
   * Measurement unit used for measuring areas and displaying information.
246
   * </p>
247
   * 
248
   * @see #getDistanceArea()
249
   * @see #setDistanceArea(int)
250
   */
251
  private int distanceArea = 1;
252

    
253
  /**
254
   * <p>
255
   * Measurement unit used by this view port for the map.
256
   * </p>
257
   * 
258
   * @see #getMapUnits()
259
   * @see #setMapUnits(int)
260
   */
261
  private int mapUnits = 1;
262

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

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

    
290
  /**
291
   * <p>
292
   * Clipping area.
293
   * </p>
294
   */
295
  // private Rectangle2D clip;
296

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

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

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

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

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

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

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

    
386
  /**
387
   * <p>
388
   * ViewPort resolution in <i>dots-per-inch</i>. Useful to calculate the
389
   * geographic scale of the view.
390
   * </p>
391
   * 
392
   * @see Toolkit#getScreenResolution()
393
   * @see MapContext#getScaleView()
394
   */
395
  private Double dpi = null;
396

    
397
  public ViewPort() {
398

    
399
  }
400

    
401
  /**
402
   * <p>
403
   * Creates a new view port with the information of the projection in
404
   * <code>proj</code> argument, and default configuration:
405
   * </p>
406
   * <p>
407
   * <ul>
408
   * <li><i><code>distanceUnits</code></i> = meters
409
   * <li><i><code>mapUnits</code></i> = meters
410
   * <li><i><code>backColor</code></i> = <i>undefined</i>
411
   * <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
412
   * </ul>
413
   * </p>
414
   * 
415
   * @param proj information of the projection for this view port
416
   */
417
  public ViewPort(IProjection proj) {
418
    // Por defecto
419
    this.proj = proj;
420
  }
421

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

    
444
  /**
445
   * <p>
446
   * Appends the specified {@link ViewPortListener ViewPortListener} listener if
447
   * weren't.
448
   * </p>
449
   * 
450
   * @param arg0 the listener to add
451
   * @return <code>true</code> if has been added successfully
452
   * @see #removeViewPortListener(ViewPortListener)
453
   */
454
  public boolean addViewPortListener(ViewPortListener arg0) {
455
    if (!listeners.contains(arg0)) {
456
      return listeners.add(arg0);
457
    }
458
    return false;
459
  }
460

    
461
  /**
462
   * <p>
463
   * Removes the specified {@link ViewPortListener ViewPortListener} listener,
464
   * if existed.
465
   * </p>
466
   * 
467
   * @param arg0 the listener to remove
468
   * @return <code>true</code> if the contained the specified listener.
469
   * @see #addViewPortListener(ViewPortListener)
470
   */
471
  public boolean removeViewPortListener(ViewPortListener arg0) {
472
    return listeners.remove(arg0);
473
  }
474

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

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

    
499
    return (int) (d * pScreen.x);
500
  }
501

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

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

    
526
    return pScreen;
527
  }
528

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

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

    
562
    return toMapPoint(pScreen);
563
  }
564

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

    
586
  /**
587
   * <p>
588
   * Converts and returns the distance <code>d</code>, that is in <i>screen
589
   * coordinates</i> to <i>map coordinates</i> using the transformation affine
590
   * information in the {@link #trans #trans} attribute.
591
   * </p>
592
   * 
593
   * @param d distance in pixels
594
   * @return distance equivalent in <i>map coordinates</i>
595
   * @see #fromMapDistance(double)
596
   * @see AffineTransform
597
   */
598
  public double toMapDistance(int d) {
599
    double dist = d / trans.getScaleX();
600

    
601
    return dist;
602
  }
603

    
604
  /**
605
   * <p>
606
   * Converts and returns the 2D point argument, that is in <i>screen
607
   * coordinates</i> (pixels) to <i>map coordinates</i> using the inverse affine
608
   * transformation of the {@link #trans #trans} attribute.
609
   * </p>
610
   * 
611
   * @param pScreen the 2D point in <i>screen coordinates</i> (pixels)
612
   * @return 2D point equivalent in <i>map coordinates</i>
613
   * @see #toMapPoint(int, int)
614
   * @see AffineTransform#createInverse()
615
   * @see AffineTransform#transform(Point2D, Point2D)
616
   * @deprecated use {@link #convertToMapPoint(Point2D)}
617
   */
618
  public Point2D toMapPoint(Point2D pScreen) {
619
    Point2D.Double pWorld = new Point2D.Double();
620
    AffineTransform at;
621

    
622
    try {
623
      at = trans.createInverse();
624
      at.transform(pScreen, pWorld);
625
    }
626
    catch (NoninvertibleTransformException e) {
627
      throw new RuntimeException("Non invertible transform Exception", e);
628
    }
629

    
630
    return pWorld;
631
  }
632

    
633
  public Point convertToMapPoint(Point2D pScreen) {
634
    Point2D p = toMapPoint(pScreen);
635
    try {
636
      return geomManager.createPoint(p.getX(), p.getY(),
637
          Geometry.SUBTYPES.GEOM2D);
638
    }
639
    catch (CreateGeometryException e) {
640
      // FIXME: Use a most especific exception.
641
      throw new RuntimeException(e);
642
    }
643
  }
644

    
645
  public Point convertToMapPoint(int x, int y) {
646
    Point2D pScreen = new Point2D.Double(x, y);
647

    
648
    return convertToMapPoint(pScreen);
649
  }
650

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

    
668
    double dist = 0;
669
    if (proj.isProjected()) {
670
      dist = pt1.distance(pt2);
671
      dist = dist * MapContext.getDistanceTrans2Meter()[getMapUnits()];
672
    }
673
    else {
674
      GeoCalc geocalc = new GeoCalc(proj);
675
      dist = geocalc.distanceVincenty(pt1, pt2);
676
    }
677
    return dist;
678
  }
679

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

    
694
  /**
695
   * <p>
696
   * Sets as envelope and adjusted envelope of this view port, the previous.
697
   * Recalculating its parameters.
698
   * Stores the current extent in the next extents of the history.
699
   * </p>
700
   * 
701
   * @see #getExtents()
702
   * @see #calculateAffineTransform()
703
   */
704
  public void setPreviousEnvelope() {
705
    this.updateDrawVersion();
706
    extentsHistory.putNext(extent);
707
    extent = extentsHistory.removePrev();
708

    
709
    // Calcula la transformaci�n af�n
710
    calculateAffineTransform();
711

    
712
    // Lanzamos los eventos de extent cambiado
713
    callExtentChanged(getAdjustedExtent());
714
  }
715

    
716
  /**
717
   * <p>
718
   * Sets as envelope and adjusted envelope of this view port, the next.
719
   * Recalculating its parameters.
720
   * Stores the current extent in the previous extents of the history.
721
   * </p>
722
   * 
723
   * @see #getExtents()
724
   * @see #calculateAffineTransform()
725
   */
726
  public void setNextEnvelope() {
727
    this.updateDrawVersion();
728
    extentsHistory.put(extent);
729
    extent = extentsHistory.removeNext();
730

    
731
    // Calcula la transformaci�n af�n
732
    calculateAffineTransform();
733

    
734
    // Lanzamos los eventos de extent cambiado
735
    callExtentChanged(getAdjustedExtent());
736
  }
737

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

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

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

    
850
    if (this.extent != null && this.extent.equals(newExtent)) {
851
      return;
852
    }
853

    
854
    if (extent != null) {
855
      if (!extent.equals(extentsHistory.get())
856
          && !extent.equals(extentsHistory.getNext())) {
857
        extentsHistory.clear();
858
      }
859
      extentsHistory.put(extent);
860
    }
861

    
862
    this.updateDrawVersion();
863
    this.extent = newExtent;
864

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

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

    
873
  /**
874
   * <p>
875
   * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
876
   * <ul>
877
   * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
878
   * scale, adjusted extent, affine transformation between map and screen
879
   * coordinates, the real world coordinates equivalent to 1 pixel, and the real
880
   * world coordinates equivalent to 3 pixels.
881
   * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered
882
   * that the extent has changed.
883
   * </ul>
884
   * </p>
885
   * 
886
   * @see #setEnvelope(Envelope)
887
   * @see #calculateAffineTransform()
888
   */
889
  public void refreshExtent() {
890
    // this.scale = scale;
891

    
892
    // Calcula la transformaci�n af�n
893
    calculateAffineTransform();
894

    
895
    // Lanzamos los eventos de extent cambiado
896
    callExtentChanged(getAdjustedExtent());
897
  }
898

    
899
  /**
900
   * <p>
901
   * Calculates and returns using the current projection of this view port, the
902
   * scale that is the extent in <i>screen coordinates</i> from the image in
903
   * <i>map coordinates</i>.
904
   * </p>
905
   * 
906
   * @return the scale <i>extent / image size</i> projected by this view port
907
   * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
908
   */
909
  private double getScale() {
910

    
911
    double[] trans2Meter = MapContext.getDistanceTrans2Meter();
912
    if (proj == null) {
913
      double wmeters = ((getImageSize().width / this.getDPI()) * 0.0254);
914
      return (long) ((trans2Meter[getMapUnits()] * getAdjustedEnvelope()
915
          .getLength(0)) / wmeters);
916
    }
917
    else {
918
      return Math.round(proj.getScale(getAdjustedEnvelope().getMinimum(0)
919
          * trans2Meter[getMapUnits()], getAdjustedEnvelope().getMaximum(0)
920
          * trans2Meter[getMapUnits()], getImageSize().width, this.getDPI()));
921
    }
922

    
923
    /*
924
     * return proj.getScale(extent.getMinX(), extent.getMaxX(), imageSize.width,
925
     * dpi);
926
     */
927
  }
928

    
929
  /**
930
   * <p>
931
   * Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D
932
   * coordinates</i> (pixels), preserving the "straightness" and "parallelism"
933
   * of the lines.
934
   * </p>
935
   * 
936
   * @return the affine transformation
937
   * @see #setAffineTransform(AffineTransform)
938
   * @see #calculateAffineTransform()
939
   */
940
  public AffineTransform getAffineTransform() {
941
    return trans;
942
  }
943

    
944
  /**
945
   * <p>
946
   * Returns the size of the image projected.
947
   * </p>
948
   * 
949
   * @return the image size
950
   * @see #setImageSize(Dimension)
951
   * @see #getImageHeight()
952
   * @see #getImageWidth()
953
   */
954
  public Dimension getImageSize() {
955
    return imageSize;
956
  }
957

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

    
970
    if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
971
      this.updateDrawVersion();
972
      this.imageSize = imageSize;
973
      calculateAffineTransform();
974
    }
975
  }
976

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

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

    
999
  /**
1000
   * <p>
1001
   * Notifies to all view port listeners registered, that the time of this view
1002
   * port has changed.
1003
   * </p>
1004
   * 
1005
   * @param newTime the new time
1006
   * @see #refreshExtent()
1007
   * @see #setTime(Time)
1008
   * @see ExtentEvent
1009
   * @see ViewPortListener
1010
   */
1011
  protected void callTimeChanged(Time newTime) {
1012
    ExtentEvent viewPortEvent = new ExtentEvent(newTime);
1013

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

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

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

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

    
1054
    for (int i = 0; i < listeners.size(); i++) {
1055
      ViewPortListener listener = (ViewPortListener) listeners.get(i);
1056
      listener.projectionChanged(ev);
1057
    }
1058
  }
1059

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

    
1094
    AffineTransform escalado = new AffineTransform();
1095
    AffineTransform translacion = new AffineTransform();
1096

    
1097
    double escalaX;
1098
    double escalaY;
1099

    
1100
    escalaX = imageSize.width / extent.getWidth();
1101
    escalaY = imageSize.height / extent.getHeight();
1102

    
1103
    double xCenter = extent.getCenterX();
1104
    double yCenter = extent.getCenterY();
1105
    double newHeight;
1106
    double newWidth;
1107

    
1108
    adjustedExtent = new Rectangle2D.Double();
1109

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

    
1137
    AffineTransform offsetTrans = new AffineTransform();
1138
    offsetTrans.setToTranslation(offset.getX(), offset.getY());
1139

    
1140
    trans.setToIdentity();
1141
    trans.concatenate(offsetTrans);
1142
    trans.concatenate(escalado);
1143

    
1144
    trans.concatenate(translacion);
1145

    
1146
    // Calculamos las distancias de 1 pixel y 3 pixel con esa
1147
    // transformaci�n
1148
    // de coordenadas, de forma que est�n precalculadas para cuando las
1149
    // necesitemos
1150
    AffineTransform at;
1151

    
1152
    try {
1153
      at = trans.createInverse();
1154

    
1155
      Point2D pPixel = new Point2D.Float(1, 1);
1156

    
1157
      Point2D.Float pProv = new Point2D.Float();
1158
      at.deltaTransform(pPixel, pProv);
1159

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

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

    
1189
  /**
1190
   * <p>
1191
   * Gets the offset.
1192
   * </p>
1193
   * <p>
1194
   * The offset is the position where start drawing the map.
1195
   * </p>
1196
   * 
1197
   * @return 2D point that represents the offset in pixels
1198
   * @see #setOffset(Point2D)
1199
   */
1200
  public Point2D getOffset() {
1201
    return offset;
1202
  }
1203

    
1204
  /**
1205
   * <p>
1206
   * Sets the background color.
1207
   * </p>
1208
   * 
1209
   * @param c the new background color
1210
   * @see #getBackColor()
1211
   */
1212
  public void setBackColor(Color c) {
1213
    if (!c.equals(this.backColor)) {
1214
      this.updateDrawVersion();
1215
      backColor = c;
1216
      callColorChanged(backColor);
1217
    }
1218
  }
1219

    
1220
  /**
1221
   * <p>
1222
   * Gets the background color.
1223
   * </p>
1224
   * 
1225
   * @return the background color of the view
1226
   * @see #setBackColor(Color)
1227
   */
1228
  public Color getBackColor() {
1229
    return backColor;
1230
  }
1231

    
1232
  /**
1233
   * <p>
1234
   * Returns the extent currently covered by the view adjusted (scaled) to the
1235
   * image size aspect.
1236
   * </p>
1237
   * 
1238
   * @return extent of the view adjusted to the image size aspect
1239
   * @see #setAdjustable(boolean)
1240
   * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1241
   */
1242
  public Envelope getAdjustedExtent() {
1243
    return getAdjustedEnvelope();
1244
  }
1245

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

    
1281
  /**
1282
   * <p>
1283
   * Returns the measurement unit of this view port used for measuring distances
1284
   * and displaying information.
1285
   * </p>
1286
   * 
1287
   * @return the measurement unit of this view used for measuring distances and
1288
   *         displaying information
1289
   * @see #setDistanceUnits(int)
1290
   */
1291
  public int getDistanceUnits() {
1292
    return distanceUnits;
1293
  }
1294

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

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

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

    
1337
  /**
1338
   * <p>
1339
   * Gets the measurement unit used by this view port for the map.
1340
   * </p>
1341
   * 
1342
   * @return Returns the current map measure unit
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 the new map measure unit
1355
   * @see #getMapUnits()
1356
   */
1357
  public void setMapUnits(int mapUnits) {
1358
    this.mapUnits = mapUnits;
1359
  }
1360

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

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

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

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

    
1443
  /**
1444
   * <p>
1445
   * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in the
1446
   * view with the current extent.
1447
   * </p>
1448
   * 
1449
   * @return the distance
1450
   * @see #setDist3pixel(double)
1451
   */
1452
  public double getDist3pixel() {
1453
    return dist3pixel;
1454
  }
1455

    
1456
  /**
1457
   * <p>
1458
   * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in the
1459
   * view with the current extent.
1460
   * </p>
1461
   * 
1462
   * @param dist3pixel the distance
1463
   * @see #getDist3pixel()
1464
   */
1465
  public void setDist3pixel(double dist3pixel) {
1466
    if (this.dist3pixel == dist3pixel) {
1467
      return;
1468
    }
1469
    this.updateDrawVersion();
1470
    this.dist3pixel = dist3pixel;
1471
  }
1472

    
1473
  /**
1474
   * <p>
1475
   * Returns the last previous extents of this view port.
1476
   * </p>
1477
   * 
1478
   * @return the last previous extents of this view port
1479
   * @see #setPreviousExtent()
1480
   * @deprecated use {@link ViewPort#getEnvelopes()}
1481
   */
1482
  public ExtentHistory getExtents() {
1483
    return getEnvelopes();
1484
  }
1485

    
1486
  /**
1487
   * <p>
1488
   * Returns the last previous extents of this view port.
1489
   * </p>
1490
   * 
1491
   * @return the last previous extents of this view port
1492
   * @see #setPreviousExtent()
1493
   */
1494
  public ExtentHistory getEnvelopes() {
1495
    return extentsHistory;
1496
  }
1497

    
1498
  /**
1499
   * <p>
1500
   * Gets the projection used in this view port.
1501
   * </p>
1502
   * 
1503
   * @return projection used in this view port
1504
   * @see #setProjection(IProjection)
1505
   */
1506
  public IProjection getProjection() {
1507
    return proj;
1508
  }
1509

    
1510
  /**
1511
   * <p>
1512
   * Sets the projection to this view port.
1513
   * </p>
1514
   * 
1515
   * @param proj the new projection
1516
   * @see #getProjection()
1517
   */
1518
  public void setProjection(IProjection proj) {
1519
    if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1520
      this.updateDrawVersion();
1521
      this.proj = proj;
1522
      callProjectionChanged(proj);
1523
    }
1524
  }
1525

    
1526
  // -----------------------------------------------------------------------------------------------------------
1527
  // NOTA PARA DESARROLLADORES SOBRE EL M�TODO
1528
  // "public void setAffineTransform(AffineTransform at)"
1529
  // ==============================================================================================
1530
  // Only used for print, should be removed, redefining the {@link
1531
  // RasterAdapter RasterAdapter} interface,
1532
  // allowing it to receive a {@link ViewPortData ViewPortData} .
1533
  // -----------------------------------------------------------------------------------------------------------
1534

    
1535
  /**
1536
   * <p>
1537
   * Sets only the affine transform to this view port, without updating
1538
   * dependent attributes.
1539
   * </p>
1540
   * <p>
1541
   * <b><i>This method could be problematic!</i></b>
1542
   * </p>
1543
   * 
1544
   * @param at the affine transform to set
1545
   * @see #getAffineTransform()
1546
   * @see #calculateAffineTransform()
1547
   */
1548
  public void setAffineTransform(AffineTransform at) {
1549
    this.trans = at;
1550
  }
1551

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

    
1613
    state.set(FIELD_ADJUSTED_EXTENT, adjustedExtent);
1614
    state.set(FIELD_BACK_COLOR, backColor);
1615
    state.set(FIELD_CLIP, cliprect);
1616
    state.set(FIELD_DIST1PIXEL, dist1pixel);
1617
    state.set(FIELD_DIST3PIXEL, dist3pixel);
1618
    state.set(FIELD_DISTANCE_UNITS, distanceUnits);
1619
    state.set(FIELD_DISTANCE_AREA, distanceArea);
1620

    
1621
    state.set(FIELD_EXTENT, extent);
1622
    state.set(FIELD_EXTENTS, extentsHistory);
1623

    
1624
    state.set(FIELD_MAP_UNITS, mapUnits);
1625
    state.set(FIELD_OFFSET, offset);
1626

    
1627
    state.set(FIELD_PROJ, proj);
1628

    
1629
    state.set(FIELD_IMAGE_SIZE, imageSize);
1630
  }
1631

    
1632
  public void loadFromState(PersistentState state) throws PersistenceException {
1633

    
1634
    adjustedExtent = (Rectangle2D) state.get(FIELD_ADJUSTED_EXTENT);
1635
    backColor = (Color) state.get(FIELD_BACK_COLOR);
1636
    cliprect = (Rectangle2D) state.get(FIELD_CLIP);
1637
    dist1pixel = state.getDouble(FIELD_DIST1PIXEL);
1638
    dist3pixel = state.getDouble(FIELD_DIST3PIXEL);
1639
    distanceUnits = state.getInt(FIELD_DISTANCE_UNITS);
1640
    extentsHistory = (ExtentHistory) state.get(FIELD_EXTENTS);
1641
    extent = (Rectangle2D) state.get(FIELD_EXTENT);
1642
    mapUnits = state.getInt(FIELD_MAP_UNITS);
1643
    offset = (Point2D) state.get(FIELD_OFFSET);
1644
    proj = (IProjection) state.get(FIELD_PROJ);
1645
    imageSize = (Dimension) state.get(FIELD_IMAGE_SIZE);
1646
    distanceArea = state.getInt(FIELD_DISTANCE_AREA);
1647

    
1648
    refreshExtent();
1649
  }
1650

    
1651
  public static class RegisterPersistence implements Callable {
1652

    
1653
    public Object call() throws Exception {
1654
      PersistenceManager manager = ToolsLocator.getPersistenceManager();
1655
      if (manager.getDefinition("ViewPort") == null) {
1656
        DynStruct definition = manager.addDefinition(ViewPort.class,
1657
            "ViewPort", "ViewPort Persistence definition", null, null);
1658

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

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

    
1665
        definition.addDynFieldObject(FIELD_CLIP)
1666
            .setClassOfValue(Rectangle2D.class).setMandatory(false);
1667

    
1668
        definition.addDynFieldDouble(FIELD_DIST1PIXEL).setMandatory(true);
1669

    
1670
        definition.addDynFieldDouble(FIELD_DIST3PIXEL).setMandatory(true);
1671

    
1672
        definition.addDynFieldInt(FIELD_DISTANCE_UNITS).setMandatory(true);
1673

    
1674
        definition.addDynFieldInt(FIELD_DISTANCE_AREA).setMandatory(false);
1675

    
1676
        definition.addDynFieldObject(FIELD_EXTENT)
1677
            .setClassOfValue(Rectangle2D.class).setMandatory(false);
1678

    
1679
        definition.addDynFieldObject(FIELD_EXTENTS)
1680
            .setClassOfValue(ExtentHistory.class).setMandatory(true);
1681

    
1682
        definition.addDynFieldInt(FIELD_MAP_UNITS).setMandatory(true);
1683

    
1684
        definition.addDynFieldObject(FIELD_OFFSET)
1685
            .setClassOfValue(Point2D.class).setMandatory(false);
1686

    
1687
        definition.addDynFieldObject(FIELD_PROJ)
1688
            .setClassOfValue(IProjection.class).setMandatory(true);
1689

    
1690
        definition.addDynFieldObject(FIELD_IMAGE_SIZE)
1691
            .setClassOfValue(Dimension.class).setMandatory(false);
1692
      }
1693
      return Boolean.TRUE;
1694
    }
1695

    
1696
  }
1697

    
1698
  /**
1699
   * Clone the view port without clone the listeners nor the extent history.
1700
   * 
1701
   * @return the cloned view port
1702
   */
1703
  public Object clone() throws CloneNotSupportedException {
1704

    
1705
    ViewPort clonedViewPort = (ViewPort) super.clone();
1706
    clonedViewPort.listeners = new ArrayList();
1707
    clonedViewPort.extentsHistory = new ExtentHistory();
1708

    
1709
    if (this.adjustedExtent != null) {
1710
      clonedViewPort.adjustedExtent = (Rectangle2D) this.adjustedExtent.clone();
1711
    }
1712

    
1713
    if (this.cliprect != null) {
1714
      clonedViewPort.cliprect = (Rectangle2D) this.cliprect.clone();
1715
    }
1716

    
1717
    if (this.extent != null) {
1718
      clonedViewPort.extent = (Rectangle2D) this.extent.clone();
1719
    }
1720
    if (this.imageSize != null) {
1721
      clonedViewPort.imageSize = (Dimension) this.imageSize.clone();
1722
    }
1723

    
1724
    if (this.offset != null) {
1725
      clonedViewPort.offset = (Point2D) this.offset.clone();
1726
    }
1727
    if (proj != null) {
1728
      clonedViewPort.proj = (IProjection) this.proj.clone();
1729
    }
1730

    
1731
    clonedViewPort.trans = (AffineTransform) this.trans.clone();
1732

    
1733
    return clonedViewPort;
1734
  }
1735

    
1736
  /**
1737
   * <p>
1738
   * Returns a <code>String</code> representation of the main values of this
1739
   * view port: <code>{@linkplain #extent}</code>,
1740
   * <code>{@linkplain #adjustedExtent}</code>,
1741
   * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>, and
1742
   * <code>{@linkplain #trans}</code>.
1743
   * </p>
1744
   * 
1745
   * @return a <code>string</code> representation of the main values of this
1746
   *         view port
1747
   */
1748
  public String toString() {
1749

    
1750
    String str;
1751
    str = "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1752
        + adjustedExtent + "\nimageSize=" + imageSize + "\nescale=" + scale
1753
        + "\ntrans=" + trans;
1754

    
1755
    return str;
1756
  }
1757

    
1758
  /**
1759
   * <p>
1760
   * Sets the position and size of the clipping rectangle.
1761
   * </p>
1762
   * 
1763
   * @param rectView the clipping rectangle to set
1764
   */
1765
  public void setClipRect(Rectangle2D rectView) {
1766
    this.updateDrawVersion();
1767
    cliprect = rectView;
1768
  }
1769

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

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

    
1809
    double x1 = (-x * difw) - x + extent.getWidth() / 2;
1810
    double y1 = (-y * difw) - y + extent.getHeight() / 2;
1811
    double w1 = extent.getWidth() * difw;
1812
    double h1 = extent.getHeight() * difw;
1813
    extent.setRect(-x1, -y1, w1, h1);
1814
  }
1815

    
1816
  public long getDrawVersion() {
1817
    return this.drawVersion;
1818
  }
1819

    
1820
  protected void updateDrawVersion() {
1821
    this.drawVersion++;
1822
  }
1823

    
1824
  public Time getTime() {
1825
    return time;
1826
  }
1827

    
1828
  public void setTime(Time time) {
1829
    this.time = time;
1830
    this.updateDrawVersion();
1831
    callTimeChanged(time);
1832
  }
1833

    
1834
  public double getDPI() {
1835
    if (this.dpi == null) {
1836
      return CompatLocator.getGraphicsUtils().getScreenDPI();
1837
    }
1838
    return this.dpi.doubleValue();
1839
  }
1840

    
1841
  public void setDPI(double dpi) {
1842
    this.dpi = new Double(dpi);
1843
  }
1844

    
1845
  public void setDPIToScreenDPI() {
1846
    this.dpi = null;
1847
  }
1848
}