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 @ 47799

History | View | Annotate | Download (59.2 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.lang.ref.WeakReference;
34
import java.util.ArrayList;
35
import java.util.Collection;
36
import java.util.Collections;
37
import java.util.ConcurrentModificationException;
38
import java.util.List;
39
import java.util.Objects;
40

    
41
import org.cresques.cts.GeoCalc;
42
import org.cresques.cts.IProjection;
43
import org.gvsig.compat.CompatLocator;
44
import org.slf4j.Logger;
45
import org.slf4j.LoggerFactory;
46

    
47
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
48
import org.gvsig.fmap.geom.Geometry;
49
import org.gvsig.fmap.geom.GeometryLocator;
50
import org.gvsig.fmap.geom.GeometryManager;
51
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
52
import org.gvsig.fmap.geom.exception.CreateGeometryException;
53
import org.gvsig.fmap.geom.primitive.Envelope;
54
import org.gvsig.fmap.geom.primitive.Point;
55
import static org.gvsig.fmap.mapcontext.MapContext.DEFAULT_SELECTION_COLOR;
56
import org.gvsig.fmap.mapcontext.events.ColorEvent;
57
import org.gvsig.fmap.mapcontext.events.ExtentEvent;
58
import org.gvsig.fmap.mapcontext.events.ProjectionEvent;
59
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListener;
60
import org.gvsig.fmap.mapcontext.events.listeners.ViewPortListenerEx1;
61
import org.gvsig.timesupport.Time;
62
import org.gvsig.tools.ToolsLocator;
63
import org.gvsig.tools.dynobject.DynStruct;
64
import org.gvsig.tools.lang.Cloneable;
65
import org.gvsig.tools.persistence.PersistenceManager;
66
import org.gvsig.tools.persistence.Persistent;
67
import org.gvsig.tools.persistence.PersistentState;
68
import org.gvsig.tools.persistence.exception.PersistenceException;
69
import org.gvsig.tools.util.Callable;
70

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

    
117
  private static final String FIELD_DISTANCE_AREA = "distanceArea";
118

    
119
  private static final String FIELD_IMAGE_SIZE = "imageSize";
120

    
121
  private static final String FIELD_PROJ = "proj";
122

    
123
  private static final String FIELD_OFFSET = "offset";
124

    
125
  private static final String FIELD_MAP_UNITS = "mapUnits";
126

    
127
  private static final String FIELD_EXTENT = "extent";
128

    
129
  private static final String FIELD_EXTENTS = "extents";
130

    
131
  private static final String FIELD_DISTANCE_UNITS = "distanceUnits";
132

    
133
  private static final String FIELD_DIST3PIXEL = "dist3pixel";
134

    
135
  private static final String FIELD_DIST1PIXEL = "dist1pixel";
136

    
137
  private static final String FIELD_CLIP = "clip";
138

    
139
  private static final String FIELD_BACK_COLOR = "backColor";
140

    
141
  private static final String FIELD_ADJUSTED_EXTENT = "adjustedExtent";
142

    
143
  private static final GeometryManager geomManager = GeometryLocator
144
      .getGeometryManager();
145

    
146
  private static final Logger logger = LoggerFactory.getLogger(ViewPort.class);
147

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

    
172
  protected Time time;
173

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

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

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

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

    
227
  /**
228
   * <p>
229
   * the affine transformation between the {@link #extent extent} in <i>map 2D
230
   * coordinates</i> to the image area in the screen, in <i>screen 2D
231
   * coordinates</i> (pixels).
232
   * </p>
233
   *
234
   * @see AffineTransform
235
   * @see #getAffineTransform()
236
   * @see #setAffineTransform(AffineTransform)
237
   * @see #calculateAffineTransform()
238
   */
239
  private AffineTransform trans = new AffineTransform();
240

    
241
  /**
242
   * <p>
243
   * Measurement unit used for measuring distances and displaying information.
244
   * </p>
245
   *
246
   * @see #getDistanceUnits()
247
   * @see #setDistanceUnits(int)
248
   */
249
  private int distanceUnits = 1;
250

    
251
  /**
252
   * <p>
253
   * Measurement unit used for measuring areas and displaying information.
254
   * </p>
255
   *
256
   * @see #getDistanceArea()
257
   * @see #setDistanceArea(int)
258
   */
259
  private int distanceArea = 1;
260

    
261
  /**
262
   * <p>
263
   * Measurement unit used by this view port for the map.
264
   * </p>
265
   *
266
   * @see #getMapUnits()
267
   * @see #setMapUnits(int)
268
   */
269
  private int mapUnits = 1;
270

    
271
  /**
272
   * <p>
273
   * Array with the {@link ViewPortListener ViewPortListener}s registered to
274
   * this view port.
275
   * </p>
276
   *
277
   * @see #addViewPortListener(ViewPortListener)
278
   * @see #removeViewPortListener(ViewPortListener)
279
   */
280
  private List<ViewPortListener> listeners = new ArrayList<>();
281

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

    
298
  /**
299
   * <p>
300
   * Clipping area.
301
   * </p>
302
   */
303
  // private Rectangle2D clip;
304

    
305
  /**
306
   * <p>
307
   * Background color of this view.
308
   * </p>
309
   *
310
   * @see #getBackColor()
311
   * @see #setBackColor(Color)
312
   */
313
  private Color backColor = null; // Color.WHITE;
314

    
315
  /**
316
   * <p>
317
   * Selection color of this view.
318
   * </p>
319
   *
320
   * @see #getSelectionColor()
321
   * @see #setSelectionColor(Color)
322
   */
323
  private Color selectionColor = MapContext.DEFAULT_SELECTION_COLOR;
324

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

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

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

    
357
  /**
358
   * <p>
359
   * Ratio between the size of <code>imageSize</code> and <code>extent</code>: <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
   * <p>
385
   * When calculates the affine transform, if
386
   * <ul>
387
   * <li><i>enabled</i>: the new <code>adjustedExtent</code> will have the (X,
388
   * Y) coordinates of the <code>extent</code> and an area that will be an scale
389
   * of the image size. That area will have different height or width (not both)
390
   * of the extent according the least ratio (height or width) in
391
   *
392
   * <pre>
393
   * image.size/extent.size&quot;
394
   * </pre>.
395
   * <li><i>disabled</i>: the new <code>adjustedExtent</code> will be like
396
   * <code>extent</code>.
397
   * </ul>
398
   * </p>
399
   *
400
   * @see #setAdjustable(boolean)
401
   */
402
  private boolean adjustableExtent = true;
403

    
404
  /**
405
   * <p>
406
   * ViewPort resolution in <i>dots-per-inch</i>. Useful to calculate the
407
   * geographic scale of the view.
408
   * </p>
409
   *
410
   * @see Toolkit#getScreenResolution()
411
   * @see MapContext#getScaleView()
412
   */
413
  private Double dpi = null;
414

    
415
  public ViewPort() {
416

    
417
  }
418

    
419
  /**
420
   * <p>
421
   * Creates a new view port with the information of the projection in
422
   * <code>proj</code> argument, and default configuration:
423
   * </p>
424
   * <p>
425
   * <ul>
426
   * <li><i><code>distanceUnits</code></i> = meters
427
   * <li><i><code>mapUnits</code></i> = meters
428
   * <li><i><code>backColor</code></i> = <i>undefined</i>
429
   * <li><i><code>offset</code></i> = <code>new Point2D.Double(0, 0);</code>
430
   * </ul>
431
   * </p>
432
   *
433
   * @param proj information of the projection for this view port
434
   */
435
  public ViewPort(IProjection proj) {
436
    // Por defecto
437
    this.proj = proj;
438
  }
439

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

    
462
    private Collection<ViewPortListener> getListeners() {
463
        List<ViewPortListener> ll = Collections.unmodifiableList(listeners);
464
        return ll;
465
    }
466
    
467
  /**
468
   * <p>
469
   * Appends the specified {@link ViewPortListener ViewPortListener} listener if
470
   * weren't.
471
   * </p>
472
   *
473
   * @param listener the listener to add
474
   * @return <code>true</code> if has been added successfully
475
   * @see #removeViewPortListener(ViewPortListener)
476
   */
477
    public boolean addViewPortListener(ViewPortListener listener) {
478
        if (listener == null) {
479
            return false;
480
        }
481
        if( this.listeners.contains(listener) )  {
482
            return false;
483
        }
484
        this.listeners.add(listener);
485
        return true;
486
    }
487

    
488
  /**
489
   * <p>
490
   * Removes the specified {@link ViewPortListener ViewPortListener} listener,
491
   * if existed.
492
   * </p>
493
   *
494
   * @param listener the listener to remove
495
   * @return <code>true</code> if the contained the specified listener.
496
   * @see #addViewPortListener(ViewPortListener)
497
   */
498
    public boolean removeViewPortListener(ViewPortListener listener) {
499
        if (listener == null) {
500
            return false;
501
        }
502
        return this.listeners.remove(listener);
503
    }
504

    
505
  /**
506
   * <p>
507
   * Converts and returns the distance <code>d</code>, that is in <i>map
508
   * coordinates</i> to <i>screen coordinates</i> using a <i>delta transform</i>
509
   * with the transformation affine information in the {@link #trans #trans}
510
   * attribute.
511
   * </p>
512
   *
513
   * @param d distance in <i>map coordinates</i>
514
   * @return distance equivalent in <i>screen coordinates</i>
515
   * @see #toMapDistance(int)
516
   * @see AffineTransform#deltaTransform(Point2D, Point2D)S
517
   */
518
  public int fromMapDistance(double d) {
519
    Point2D.Double pWorld = new Point2D.Double(1, 1);
520
    Point2D.Double pScreen = new Point2D.Double();
521

    
522
    try {
523
      trans.deltaTransform(pWorld, pScreen);
524
    }
525
    catch (Exception e) {
526
      System.err.print(e.getMessage());
527
    }
528

    
529
    return (int) (d * pScreen.x);
530
  }
531

    
532
  /**
533
   * <p>
534
   * Converts and returns the 2D point <code>(x,y)</code>, that is in <i>map
535
   * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
536
   * transformation in the {@link #trans #trans} attribute.
537
   * </p>
538
   *
539
   * @param x the <code>x</code> <i>map coordinate</i> of a 2D point
540
   * @param y the <code>y</code> <i>map coordinate</i> of a 2D point
541
   * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
542
   * @see #fromMapPoint(Point2D)
543
   * @see AffineTransform#transform(Point2D, Point2D)
544
   */
545
  public Point2D fromMapPoint(double x, double y) {
546
    Point2D.Double pWorld = new Point2D.Double(x, y);
547
    Point2D.Double pScreen = new Point2D.Double();
548

    
549
    try {
550
      trans.transform(pWorld, pScreen);
551
    }
552
    catch (Exception e) {
553
      System.err.print(e.getMessage());
554
    }
555

    
556
    return pScreen;
557
  }
558

    
559
  /**
560
   * <p>
561
   * Converts and returns the 2D point argument, that is in <i>map
562
   * coordinates</i> to <i>screen coordinates</i> (pixels) using the affine
563
   * transformation in the {@link #trans #trans} attribute.
564
   * </p>
565
   *
566
   * @param point the 2D point in <i>map coordinates</i>
567
   * @return 2D point equivalent in <i>screen coordinates</i> (pixels)
568
   * @see #toMapPoint(Point2D)
569
   * @see #fromMapPoint(double, double)
570
   */
571
  public Point2D fromMapPoint(Point2D point) {
572
    return fromMapPoint(point.getX(), point.getY());
573
  }
574

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

    
592
    return toMapPoint(pScreen);
593
  }
594

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

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

    
631
    return dist;
632
  }
633

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

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

    
663
    return pWorld;
664
  }
665

    
666
  public Point convertToMapPoint(Point2D pScreen) {
667
    Point2D p = toMapPoint(pScreen);
668
    try {
669
        Point point = geomManager.createPoint(
670
                p.getX(), 
671
                p.getY(),
672
                Geometry.SUBTYPES.GEOM2D
673
        );
674
        point.setProjection(this.getProjection());
675
        return point;
676
    }
677
    catch (CreateGeometryException e) {
678
      // FIXME: Use a most especific exception.
679
      throw new RuntimeException(e);
680
    }
681
  }
682

    
683
  public Point convertToMapPoint(int x, int y) {
684
    Point2D pScreen = new Point2D.Double(x, y);
685

    
686
    return convertToMapPoint(pScreen);
687
  }
688

    
689
  /**
690
   * <p>
691
   * Returns the real distance (in <i>world coordinates</i>) at the graphic
692
   * layers of two 2D points (in <i>map coordinates</i>) of the plane where is
693
   * selected the <i>extent</i>.
694
   * </p>
695
   * <p>
696
   * If the projection of this view is UTM, considers the Earth curvature.
697
   * </p>
698
   *
699
   * @param pt1 a 2D point in <i>map coordinates</i>
700
   * @param pt2 another 2D point in <i>map coordinates</i>
701
   * @return the distance in meters between the two points 2D
702
   * @see GeoCalcImpl#distanceVincenty(Point2D, Point2D)
703
   */
704
  public double distanceWorld(Point2D pt1, Point2D pt2) {
705

    
706
    double dist = 0;
707
    if (proj.isProjected()) {
708
      dist = pt1.distance(pt2);
709
      dist = dist * MapContext.getDistanceTrans2Meter()[getMapUnits()];
710
    }
711
    else {
712
      GeoCalc geocalc = new GeoCalc(proj);
713
      dist = geocalc.distanceVincenty(pt1, pt2);
714
    }
715
    return dist;
716
  }
717

    
718
  /**
719
   * <p>
720
   * Sets as extent and adjusted extent of this view port, the previous.
721
   * Recalculating its parameters.
722
   * </p>
723
   *
724
   * @see #getExtents()
725
   * @see #calculateAffineTransform()
726
   * @deprecated use {@link ViewPort#setPreviousEnvelope()}
727
   */
728
  public void setPreviousExtent() {
729
    setPreviousEnvelope();
730
  }
731

    
732
  /**
733
   * <p>
734
   * Sets as envelope and adjusted envelope of this view port, the previous.
735
   * Recalculating its parameters.
736
   * Stores the current extent in the next extents of the history.
737
   * </p>
738
   *
739
   * @see #getExtents()
740
   * @see #calculateAffineTransform()
741
   */
742
  public void setPreviousEnvelope() {
743
    this.updateDrawVersion();
744

    
745
//    extentsHistory.putNext(extent);
746
//    extent = extentsHistory.removePrev();
747
    extent = extentsHistory.setPreviousExtent();
748

    
749
    // Calcula la transformaci?n af?n
750
    calculateAffineTransform();
751

    
752
    // Lanzamos los eventos de extent cambiado
753
    callExtentChanged(getAdjustedExtent());
754
  }
755

    
756
  /**
757
   * <p>
758
   * Sets as envelope and adjusted envelope of this view port, the next.
759
   * Recalculating its parameters.
760
   * Stores the current extent in the previous extents of the history.
761
   * </p>
762
   *
763
   * @see #getExtents()
764
   * @see #calculateAffineTransform()
765
   */
766
  public void setNextEnvelope() {
767
    this.updateDrawVersion();
768

    
769
    extent = extentsHistory.setNextExtent();
770

    
771
    // Calcula la transformaci?n af?n
772
    calculateAffineTransform();
773

    
774
    // Lanzamos los eventos de extent cambiado
775
    callExtentChanged(getAdjustedExtent());
776
  }
777

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

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

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

    
896
    if (this.extent != null && this.extent.equals(newExtent)) {
897
      return;
898
    }
899

    
900
    this.updateDrawVersion();
901
    this.extent = newExtent;
902
    try {
903
        calculateAffineTransform();
904
    } catch(Exception ex) {
905
        this.extent = null;
906
        throw ex;
907
    }
908
    extentsHistory.put(extent);
909

    
910

    
911
    // Lanzamos los eventos de extent cambiado
912
    callExtentChanged(getAdjustedExtent());
913
  }
914

    
915
  /**
916
   * <p>
917
   * Changes the <i>extent</i> and <i>adjusted extent</i> of this view port:<br>
918
   * <ul>
919
   * <li>Executes {@linkplain #calculateAffineTransform()}: getting the new
920
   * scale, adjusted extent, affine transformation between map and screen
921
   * coordinates, the real world coordinates equivalent to 1 pixel, and the real
922
   * world coordinates equivalent to 3 pixels.
923
   * <li>Notifies to all {@link ViewPortListener ViewPortListener} registered
924
   * that the extent has changed.
925
   * </ul>
926
   * </p>
927
   *
928
   * @see #setEnvelope(Envelope)
929
   * @see #calculateAffineTransform()
930
   */
931
  public void refreshExtent() {
932
    //Por compatibilidad con versiones anteriores a la introducci?n de las lista de zooms siguientes
933
    if (extentsHistory.getCurrent() == null) {
934
      extentsHistory.put(extent);
935
    } else {
936
      extent = extentsHistory.getCurrent();
937
    }
938

    
939
    // Calcula la transformaci?n af?n
940
    calculateAffineTransform();
941

    
942
    // Lanzamos los eventos de extent cambiado
943
    callExtentChanged(getAdjustedExtent());
944
  }
945

    
946
  /**
947
   * <p>
948
   * Calculates and returns using the current projection of this view port, the
949
   * scale that is the extent in <i>screen coordinates</i> from the image in
950
   * <i>map coordinates</i>.
951
   * </p>
952
   *
953
   * @return the scale <i>extent / image size</i> projected by this view port
954
   * @deprecated since 07/09/07, use {@linkplain MapContext#getScaleView()}
955
   */
956
  private double getScale() {
957

    
958
    double[] trans2Meter = MapContext.getDistanceTrans2Meter();
959
    if (proj == null) {
960
      double wmeters = ((getImageSize().width / this.getDPI()) * 0.0254);
961
      return (long) ((trans2Meter[getMapUnits()] * getAdjustedEnvelope()
962
          .getLength(0)) / wmeters);
963
    }
964
    else {
965
      return Math.round(proj.getScale(getAdjustedEnvelope().getMinimum(0)
966
          * trans2Meter[getMapUnits()], getAdjustedEnvelope().getMaximum(0)
967
          * trans2Meter[getMapUnits()], getImageSize().width, this.getDPI()));
968
    }
969

    
970
    /*
971
     * return proj.getScale(extent.getMinX(), extent.getMaxX(), imageSize.width,
972
     * dpi);
973
     */
974
  }
975

    
976
  /**
977
   * <p>
978
   * Affine transformation between <i>map 2D coordinates</i> to <i>screen 2D
979
   * coordinates</i> (pixels), preserving the "straightness" and "parallelism"
980
   * of the lines.
981
   * </p>
982
   *
983
   * @return the affine transformation
984
   * @see #setAffineTransform(AffineTransform)
985
   * @see #calculateAffineTransform()
986
   */
987
  public AffineTransform getAffineTransform() {
988
    return trans;
989
  }
990

    
991
  /**
992
   * <p>
993
   * Returns the size of the image projected.
994
   * </p>
995
   *
996
   * @return the image size
997
   * @see #setImageSize(Dimension)
998
   * @see #getImageHeight()
999
   * @see #getImageWidth()
1000
   */
1001
  public Dimension getImageSize() {
1002
    return imageSize;
1003
  }
1004

    
1005
  /**
1006
   * <p>
1007
   * Sets the size of the image projected, recalculating the parameters of this
1008
   * view port.
1009
   * </p>
1010
   *
1011
   * @param imageSize the image size
1012
   * @see #getImageSize()
1013
   * @see #calculateAffineTransform()
1014
   */
1015
  public void setImageSize(Dimension imageSize) {
1016

    
1017
    if (this.imageSize == null || (!this.imageSize.equals(imageSize))) {
1018
      this.updateDrawVersion();
1019
      this.imageSize = imageSize;
1020
      calculateAffineTransform();
1021
    }
1022
  }
1023

    
1024
  /**
1025
   * <p>
1026
   * Notifies to all view port listeners registered, that the adjusted extent of
1027
   * this view port has changed.
1028
   * </p>
1029
   *
1030
   * @param newRect the new adjusted extend
1031
   * @see #refreshExtent()
1032
   * @see #setEnvelope(Envelope)
1033
   * @see #setPreviousExtent()
1034
   * @see ExtentEvent
1035
   * @see ViewPortListener
1036
   */
1037
    protected void callExtentChanged(Envelope newRect) {
1038
        ExtentEvent ev = ExtentEvent.createExtentEvent(newRect);
1039

    
1040
        for (ViewPortListener listener : this.getListeners()) {
1041
            if (listener != null) {
1042
                listener.extentChanged(ev);
1043
            }
1044
        }
1045
    }
1046

    
1047
  /**
1048
   * <p>
1049
   * Notifies to all view port listeners registered, that the time of this view
1050
   * port has changed.
1051
   * </p>
1052
   *
1053
   * @param newTime the new time
1054
   * @see #refreshExtent()
1055
   * @see #setTime(Time)
1056
   * @see ExtentEvent
1057
   * @see ViewPortListener
1058
   */
1059
    protected void callTimeChanged(Time newTime) {
1060
        ExtentEvent viewPortEvent = new ExtentEvent(newTime);
1061

    
1062
        for (ViewPortListener listener : this.getListeners()) {
1063
            if (listener != null) {
1064
                listener.extentChanged(viewPortEvent);
1065
            }
1066
        }
1067
    }
1068

    
1069
  /**
1070
   * <p>
1071
   * Notifies to all view port listeners registered, that the background color
1072
   * of this view port has changed.
1073
   * </p>
1074
   *
1075
   * @param c the new background color
1076
   * @see #setBackColor(Color)
1077
   * @see ColorEvent
1078
   * @see ViewPortListener
1079
   */
1080
    private void callColorChanged(Color c) {
1081
        ColorEvent ce = ColorEvent.createColorEvent(c);
1082

    
1083
        for (ViewPortListener listener : this.getListeners()) {
1084
            if (listener != null) {
1085
                listener.backColorChanged(ce);
1086
            }
1087
        }
1088
    }
1089

    
1090
  /**
1091
   * <p>
1092
   * Notifies to all view port listeners registered, that the background color
1093
   * of this view port has changed.
1094
   * </p>
1095
   *
1096
   * @param c the new background color
1097
   * @see #setBackColor(Color)
1098
   * @see ColorEvent
1099
   * @see ViewPortListener
1100
   */
1101
    private void callSelectionColorChanged(Color c) {
1102
        ColorEvent ce = ColorEvent.createColorEvent(c);
1103

    
1104
        for (ViewPortListener listener : this.getListeners()) {
1105
            if (listener != null) {
1106
                if(listener instanceof ViewPortListenerEx1){
1107
                    ((ViewPortListenerEx1)listener).selectionColorChanged(ce);
1108
                }
1109
            }
1110
        }
1111
    }
1112

    
1113
  /**
1114
   * <p>
1115
   * Notifies to all view port listeners registered, that the projection of this
1116
   * view port has changed.
1117
   * </p>
1118
   *
1119
   * @param projection the new projection
1120
   * @see #setProjection(IProjection)
1121
   * @see ProjectionEvent
1122
   * @see ViewPortListener
1123
   */
1124
    private void callProjectionChanged(IProjection projection) {
1125
        ProjectionEvent ev = ProjectionEvent.createProjectionEvent(projection);
1126

    
1127
        for (ViewPortListener listener : this.getListeners()) {
1128
            if (listener != null) {
1129
                listener.projectionChanged(ev);
1130
            }
1131
        }
1132
    }
1133

    
1134
  /**
1135
   * <p>
1136
   * Calculates the affine transformation between the {@link #extent extent} in
1137
   * <i>map 2D coordinates</i> to the image area in the screen, in <i>screen 2D
1138
   * coordinates</i> (pixels).
1139
   * </p>
1140
   * <p>
1141
   * This process recalculates some parameters of this view port:<br>
1142
   * <ul>
1143
   * <li>The new {@link #scale scale} .
1144
   * <li>The new {@link #adjustedExtent adjustedExtent} .
1145
   * <li>The new {@link #trans trans} .
1146
   * <li>The new real world coordinates equivalent to 1 pixel (
1147
   * {@link #dist1pixel dist1pixel}) .
1148
   * <li>The new real world coordinates equivalent to 3 pixels (
1149
   * {@link #dist3pixel dist3pixel}) .
1150
   * </ul>
1151
   * </p>
1152
   *
1153
   * @see #getAffineTransform()
1154
   * @see #setAffineTransform(AffineTransform)
1155
   * @see #refreshExtent()
1156
   * @see #setEnvelope(Envelope)
1157
   * @see #setImageSize(Dimension)
1158
   * @see #setPreviousExtent()
1159
   * @see #createFromXML(XMLEntity)
1160
   * @see AffineTransform
1161
   */
1162
  private void calculateAffineTransform() {
1163
    if ((imageSize == null) || (extent == null) || (imageSize.width <= 0)
1164
        || (imageSize.height <= 0)) {
1165
      return;
1166
    }
1167

    
1168
    AffineTransform escalado = new AffineTransform();
1169
    AffineTransform translacion = new AffineTransform();
1170

    
1171
    double escalaX;
1172
    double escalaY;
1173

    
1174
    escalaX = imageSize.width / extent.getWidth();
1175
    escalaY = imageSize.height / extent.getHeight();
1176

    
1177
    double xCenter = extent.getCenterX();
1178
    double yCenter = extent.getCenterY();
1179
    double newHeight;
1180
    double newWidth;
1181

    
1182
    adjustedExtent = new Rectangle2D.Double();
1183

    
1184
    if (adjustableExtent) {
1185
      if (escalaX < escalaY) {
1186
        scale = escalaX;
1187
        newHeight = imageSize.height / scale;
1188
        adjustedExtent.setRect(xCenter - (extent.getWidth() / 2.0), yCenter
1189
            - (newHeight / 2.0), extent.getWidth(), newHeight);
1190
      }
1191
      else {
1192
        scale = escalaY;
1193
        newWidth = imageSize.width / scale;
1194
        adjustedExtent.setRect(xCenter - (newWidth / 2.0),
1195
            yCenter - (extent.getHeight() / 2.0), newWidth, extent.getHeight());
1196
      }
1197
      escalado.setToScale(scale, -scale);
1198
    }
1199
    else { // adjusted is same as extent
1200
      scale = escalaX;
1201
      adjustedExtent.setFrame(extent);
1202
      escalado.setToScale(escalaX, -escalaY);
1203
    }
1204
    Envelope env = getAdjustedExtent();
1205
    if (env == null) {
1206
      return;
1207
    }
1208
    translacion.setToTranslation(-env.getMinimum(0), -env.getMinimum(1)
1209
        - getAdjustedExtent().getLength(1));
1210

    
1211
    AffineTransform offsetTrans = new AffineTransform();
1212
    offsetTrans.setToTranslation(offset.getX(), offset.getY());
1213

    
1214
    trans.setToIdentity();
1215
    trans.concatenate(offsetTrans);
1216
    trans.concatenate(escalado);
1217

    
1218
    trans.concatenate(translacion);
1219

    
1220
    // Calculamos las distancias de 1 pixel y 3 pixel con esa
1221
    // transformaci?n
1222
    // de coordenadas, de forma que est?n precalculadas para cuando las
1223
    // necesitemos
1224
    AffineTransform at;
1225

    
1226
    try {
1227
      at = trans.createInverse();
1228

    
1229
      Point2D pPixel = new Point2D.Float(1, 1);
1230

    
1231
      Point2D.Float pProv = new Point2D.Float();
1232
      at.deltaTransform(pPixel, pProv);
1233

    
1234
      dist1pixel = pProv.x;
1235
      dist3pixel = 3 * pProv.x;
1236
    }
1237
    catch (NoninvertibleTransformException e) {
1238
      String msg = "Can't calculate affine transform for the view port." + "\n" +
1239
        "The extent can be out of range for this projection." + "\n" +
1240
        "transformada afin: " + Objects.toString(trans) + "\n" +
1241
        "extent: " + Objects.toString(extent) + "\n" +
1242
        "imageSize: " + Objects.toString(imageSize) + "\n" +
1243
        "projection: " + Objects.toString(proj) + "\n" +
1244
        e.getLocalizedMessage() 
1245
      ;
1246
      logger.error(msg, e);
1247
      trans.setToIdentity();
1248
      scale = 0;
1249
      adjustedExtent = null;
1250
      adjustableExtent = true;
1251
      throw new RuntimeException(msg, e);
1252
    }
1253
  }
1254

    
1255
  /**
1256
   * <p>
1257
   * Sets the offset.
1258
   * </p>
1259
   * <p>
1260
   * The offset is the position where start drawing the map.
1261
   * </p>
1262
   *
1263
   * @param p 2D point that represents the offset in pixels
1264
   * @see #getOffset()
1265
   */
1266
  public void setOffset(Point2D p) {
1267
    if (!offset.equals(p)) {
1268
      this.updateDrawVersion();
1269
      offset = p;
1270
    }
1271
  }
1272

    
1273
  /**
1274
   * <p>
1275
   * Gets the offset.
1276
   * </p>
1277
   * <p>
1278
   * The offset is the position where start drawing the map.
1279
   * </p>
1280
   *
1281
   * @return 2D point that represents the offset in pixels
1282
   * @see #setOffset(Point2D)
1283
   */
1284
  public Point2D getOffset() {
1285
    return offset;
1286
  }
1287

    
1288
  /**
1289
   * <p>
1290
   * Sets the background color.
1291
   * </p>
1292
   *
1293
   * @param c the new background color
1294
   * @see #getBackColor()
1295
   */
1296
  public void setBackColor(Color c) {
1297
    if (!c.equals(this.backColor)) {
1298
      this.updateDrawVersion();
1299
      backColor = c;
1300
      callColorChanged(backColor);
1301
    }
1302
  }
1303

    
1304
  /**
1305
   * <p>
1306
   * Sets the selection color.
1307
   * </p>
1308
   *
1309
   * @param c the new selection color
1310
   * @see #getSelectionColor()
1311
   */
1312
  public void setSelectionColor(Color c) {
1313
    if (!c.equals(this.selectionColor)) {
1314
      this.updateDrawVersion();
1315
      selectionColor = c;
1316
      callSelectionColorChanged(selectionColor);
1317
    }
1318
  }
1319

    
1320
  /**
1321
   * <p>
1322
   * Gets the background color.
1323
   * </p>
1324
   *
1325
   * @return the background color of the view
1326
   * @see #setBackColor(Color)
1327
   */
1328
  public Color getBackColor() {
1329
    return backColor;
1330
  }
1331

    
1332
  /**
1333
   * <p>
1334
   * Gets the selection color.
1335
   * </p>
1336
   *
1337
   * @return the selection color of the view
1338
   * @see #setSelectionColor(Color)
1339
   */
1340
  public Color getSelectionColor() {
1341
    return selectionColor;
1342
  }
1343

    
1344
  /**
1345
   * <p>
1346
   * Returns the extent currently covered by the view adjusted (scaled) to the
1347
   * image size aspect.
1348
   * </p>
1349
   *
1350
   * @return extent of the view adjusted to the image size aspect
1351
   * @see #setAdjustable(boolean)
1352
   * @deprecated use {@link ViewPort#getAdjustedEnvelope()} instead
1353
   */
1354
  public Envelope getAdjustedExtent() {
1355
    return getAdjustedEnvelope();
1356
  }
1357

    
1358
  /**
1359
   * <p>
1360
   * Returns the envelope currently covered by the view adjusted (scaled) to the
1361
   * image size aspect.
1362
   * </p>
1363
   *
1364
   * @return envelope of the view adjusted to the image size aspect
1365
   * @see #setAdjustable(boolean)
1366
   */
1367
    public Envelope getAdjustedEnvelope() {
1368
        if (adjustedExtent == null) {
1369
            calculateAffineTransform();
1370
        }
1371
        if (cliprect != null) {
1372
            Rectangle2D r = adjustedExtent.createIntersection(cliprect);
1373
            try {
1374
                return geomManager.createEnvelope(r.getX(), r.getY(), r.getMaxX(), r.getMaxY(), SUBTYPES.GEOM2D);
1375
            } catch (CreateEnvelopeException e) {
1376
                e.printStackTrace();
1377
                logger.error("Error adjusting the extent", e);
1378
            }
1379
        }
1380
        if (adjustedExtent != null) {
1381
            try {
1382
                return geomManager.createEnvelope(adjustedExtent.getX(), adjustedExtent.getY(),
1383
                    adjustedExtent.getMaxX(), adjustedExtent.getMaxY(), SUBTYPES.GEOM2D);
1384
            } catch (CreateEnvelopeException e) {
1385
                e.printStackTrace();
1386
                logger.error("Error adjusting the extent", e);
1387
            }
1388
        }
1389
        return null;
1390
    }
1391

    
1392
  /**
1393
   * <p>
1394
   * Returns the measurement unit of this view port used for measuring distances
1395
   * and displaying information.
1396
   * </p>
1397
   *
1398
   * @return the measurement unit of this view used for measuring distances and
1399
   *         displaying information
1400
   * @see #setDistanceUnits(int)
1401
   */
1402
  public int getDistanceUnits() {
1403
    return distanceUnits;
1404
  }
1405

    
1406
  /**
1407
   * <p>
1408
   * Returns the measurement unit of this view port used for measuring areas and
1409
   * displaying information.
1410
   * </p>
1411
   *
1412
   * @return the measurement unit of this view used for measuring areas and
1413
   *         displaying information
1414
   * @see #setDistanceUnits(int)
1415
   */
1416
  public int getDistanceArea() {
1417
    return distanceArea;
1418
  }
1419

    
1420
  /**
1421
   * <p>
1422
   * Sets the measurement unit of this view port used for measuring distances
1423
   * and displaying information.
1424
   * </p>
1425
   *
1426
   * @param distanceUnits the measurement unit of this view used for measuring
1427
   *          distances and displaying information
1428
   * @see #getDistanceUnits()
1429
   */
1430
  public void setDistanceUnits(int distanceUnits) {
1431
    this.distanceUnits = distanceUnits;
1432
  }
1433

    
1434
  /**
1435
   * <p>
1436
   * Sets the measurement unit of this view port used for measuring areas and
1437
   * displaying information.
1438
   * </p>
1439
   *
1440
   * @param distanceUnits the measurement unit of this view used for measuring
1441
   *          areas and displaying information
1442
   * @see #getDistanceUnits()
1443
   */
1444
  public void setDistanceArea(int distanceArea) {
1445
    this.distanceArea = distanceArea;
1446
  }
1447

    
1448
  /**
1449
   * <p>
1450
   * Gets the measurement unit used by this view port for the map.
1451
   * </p>
1452
   *
1453
   * @return Returns the current map measure unit
1454
   * @see #setMapUnits(int)
1455
   */
1456
  public int getMapUnits() {
1457
    return mapUnits;
1458
  }
1459

    
1460
  /**
1461
   * <p>
1462
   * Sets the measurement unit used by this view port for the map.
1463
   * </p>
1464
   *
1465
   * @param mapUnits the new map measure unit
1466
   * @see #getMapUnits()
1467
   */
1468
  public void setMapUnits(int mapUnits) {
1469
    this.mapUnits = mapUnits;
1470
  }
1471

    
1472
  /**
1473
   * <p>
1474
   * Gets the width in <i>screen coordinates</i> of the rectangle where the
1475
   * image is displayed.
1476
   * </p>
1477
   * <p>
1478
   * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1479
   * <ul>
1480
   * <li>The new {@link #scale scale} .
1481
   * <li>The new {@link #adjustedExtent adjustableExtent} .
1482
   * <li>The new {@link #trans trans} .
1483
   * <li>The new real world coordinates equivalent to 1 pixel (
1484
   * {@link #dist1pixel dist1pixel}) .
1485
   * <li>The new real world coordinates equivalent to 3 pixels (
1486
   * {@link #dist3pixel dist3pixel}) .
1487
   * </ul>
1488
   * </p>
1489
   *
1490
   * @see #getImageHeight()
1491
   * @see #getImageSize()
1492
   * @see #setImageSize(Dimension)
1493
   */
1494
  public int getImageWidth() {
1495
    return imageSize.width;
1496
  }
1497

    
1498
  /**
1499
   * <p>
1500
   * Gets the height in <i>screen coordinates</i> of the rectangle where the
1501
   * image is displayed.
1502
   * </p>
1503
   * <p>
1504
   * Used by {@linkplain #calculateAffineTransform()} to calculate:<br>
1505
   * <ul>
1506
   * <li>The new {@link #scale scale} .
1507
   * <li>The new {@link #adjustedExtent adjustableExtent} .
1508
   * <li>The new {@link #trans trans} .
1509
   * <li>The new real world coordinates equivalent to 1 pixel (
1510
   * {@link #dist1pixel dist1pixel}) .
1511
   * <li>The new real world coordinates equivalent to 3 pixels (
1512
   * {@link #dist3pixel dist3pixel}) .
1513
   * </ul>
1514
   * </p>
1515
   *
1516
   * @see #getImageWidth()
1517
   * @see #getImageSize()
1518
   * @see #setImageSize(Dimension)
1519
   */
1520
  public int getImageHeight() {
1521
    return imageSize.height;
1522
  }
1523

    
1524
  /**
1525
   * <p>
1526
   * Gets the distance in <i>world coordinates</i> equivalent to 1 pixel in the
1527
   * view with the current extent.
1528
   * </p>
1529
   *
1530
   * @return the distance
1531
   * @see #setDist1pixel(double)
1532
   */
1533
  public double getDist1pixel() {
1534
    return dist1pixel;
1535
  }
1536

    
1537
  /**
1538
   * <p>
1539
   * Sets the distance in <i>world coordinates</i> equivalent to 1 pixel in the
1540
   * view with the current extent.
1541
   * </p>
1542
   *
1543
   * @param dist1pixel the distance
1544
   * @see #getDist1pixel()
1545
   */
1546
  public void setDist1pixel(double dist1pixel) {
1547
    if (dist1pixel == this.dist1pixel) {
1548
      return;
1549
    }
1550
    this.updateDrawVersion();
1551
    this.dist1pixel = dist1pixel;
1552
  }
1553

    
1554
  /**
1555
   * <p>
1556
   * Gets the distance in <i>world coordinates</i> equivalent to 3 pixels in the
1557
   * view with the current extent.
1558
   * </p>
1559
   *
1560
   * @return the distance
1561
   * @see #setDist3pixel(double)
1562
   */
1563
  public double getDist3pixel() {
1564
    return dist3pixel;
1565
  }
1566

    
1567
  /**
1568
   * <p>
1569
   * Sets the distance in <i>world coordinates</i> equivalent to 3 pixels in the
1570
   * view with the current extent.
1571
   * </p>
1572
   *
1573
   * @param dist3pixel the distance
1574
   * @see #getDist3pixel()
1575
   */
1576
  public void setDist3pixel(double dist3pixel) {
1577
    if (this.dist3pixel == dist3pixel) {
1578
      return;
1579
    }
1580
    this.updateDrawVersion();
1581
    this.dist3pixel = dist3pixel;
1582
  }
1583

    
1584
  /**
1585
   * <p>
1586
   * Returns the last previous extents of this view port.
1587
   * </p>
1588
   *
1589
   * @return the last previous extents of this view port
1590
   * @see #setPreviousExtent()
1591
   * @deprecated use {@link ViewPort#getEnvelopes()}
1592
   */
1593
  public ExtentHistory getExtents() {
1594
    return getEnvelopes();
1595
  }
1596

    
1597
  /**
1598
   * <p>
1599
   * Returns the last previous extents of this view port.
1600
   * </p>
1601
   *
1602
   * @return the last previous extents of this view port
1603
   * @see #setPreviousExtent()
1604
   */
1605
  public ExtentHistory getEnvelopes() {
1606
    return extentsHistory;
1607
  }
1608

    
1609
  /**
1610
   * <p>
1611
   * Gets the projection used in this view port.
1612
   * </p>
1613
   *
1614
   * @return projection used in this view port
1615
   * @see #setProjection(IProjection)
1616
   */
1617
  public IProjection getProjection() {
1618
    return proj;
1619
  }
1620

    
1621
  /**
1622
   * <p>
1623
   * Sets the projection to this view port.
1624
   * </p>
1625
   *
1626
   * @param proj the new projection
1627
   * @see #getProjection()
1628
   */
1629
  public void setProjection(IProjection proj) {
1630
    if (this.proj == null || !this.proj.getAbrev().equals(proj.getAbrev())) {
1631
        this.updateDrawVersion();
1632
        this.proj = proj;
1633
        int metros = MapContext.getDistancePosition("Metros");
1634
        int grados = MapContext.getDistancePosition("Grados");
1635
        if (proj.isProjected()) {
1636
            if (this.getMapUnits() == grados) {
1637
                this.setMapUnits(metros);
1638
            }
1639
        } else {
1640
            this.setMapUnits(grados);
1641
        }
1642

    
1643
        callProjectionChanged(proj);
1644
    }
1645
  }
1646

    
1647
  // -----------------------------------------------------------------------------------------------------------
1648
  // NOTA PARA DESARROLLADORES SOBRE EL M?TODO
1649
  // "public void setAffineTransform(AffineTransform at)"
1650
  // ==============================================================================================
1651
  // Only used for print, should be removed, redefining the {@link
1652
  // RasterAdapter RasterAdapter} interface,
1653
  // allowing it to receive a {@link ViewPortData ViewPortData} .
1654
  // -----------------------------------------------------------------------------------------------------------
1655

    
1656
  /**
1657
   * <p>
1658
   * Sets only the affine transform to this view port, without updating
1659
   * dependent attributes.
1660
   * </p>
1661
   * <p>
1662
   * <b><i>This method could be problematic!</i></b>
1663
   * </p>
1664
   *
1665
   * @param at the affine transform to set
1666
   * @see #getAffineTransform()
1667
   * @see #calculateAffineTransform()
1668
   */
1669
  public void setAffineTransform(AffineTransform at) {
1670
    this.trans = at;
1671
  }
1672

    
1673
  /**
1674
   * <p>
1675
   * Returns an XML entity that represents this view port instance:<br>
1676
   * <ul>
1677
   * <li>Properties:
1678
   * <ul>
1679
   * <li><i>className</i>: name of this class.
1680
   * <li>If defined, the adjusted extent:
1681
   * <ul>
1682
   * <li><i>adjustedExtentX</i>: X coordinate of the adjusted extent.
1683
   * <li><i>adjustedExtentY</i>: Y coordinate of the adjusted extent.
1684
   * <li><i>adjustedExtentW</i>: width of the adjusted extent.
1685
   * <li><i>adjustedExtentH</i>: height of the adjusted extent.
1686
   * </ul>
1687
   * <li>If defined, the background color:
1688
   * <ul>
1689
   * <li><i>backColor</i>: background color.
1690
   * </ul>
1691
   * <li>If defined, the clip:
1692
   * <ul>
1693
   * <li><i>clipX</i>: X coordinate of the clip.
1694
   * <li><i>clipY</i>: Y coordinate of clip.
1695
   * <li><i>clipW</i>: width of the clip.
1696
   * <li><i>clipH</i>: height of the clip.
1697
   * </ul>
1698
   * <li><i>dist1pixel</i>: the distance in world coordinates equivalent to 1
1699
   * pixel in the view.
1700
   * <li><i>dist3pixel</i>: the distance in world coordinates equivalent to 3
1701
   * pixels in the view.
1702
   * <li><i>distanceUnits</i>: the distance measurement unit.
1703
   * <li>If defined, the extent:
1704
   * <ul>
1705
   * <li><i>extentX</i>: X coordinate of the extent.
1706
   * <li><i>extentY</i>: Y coordinate of the extent.
1707
   * <li><i>extentW</i>: width of the extent.
1708
   * <li><i>extentH</i>: height of the extent.
1709
   * </ul>
1710
   * <li><i>mapUnits</i>: the map measurement unit.
1711
   * <li><i>offsetX</i>: X coordinate of the offset.
1712
   * <li><i>offsetY</i>: Y coordinate of the offset.
1713
   * <li>If defined, the projection:
1714
   * <ul>
1715
   * <li>If its defined, the projection:
1716
   * <ul>
1717
   * <li><i>proj</i>: the projection.</li>
1718
   * </ul>
1719
   * </ul>
1720
   * <li><i>scale</i>: ratio between the size of <code>imageSize</code> and
1721
   * <code>extent</code>.
1722
   * </ul>
1723
   * <li>Child branches:
1724
   * <ul>
1725
   * <li>XML entity of the internal {@link ExtentHistory ExtentHistory} .
1726
   * </ul>
1727
   * </ul>
1728
   *
1729
   * @return the XML entity
1730
   * @see #createFromXML(XMLEntity)
1731
   */
1732
  public void saveToState(PersistentState state) throws PersistenceException {
1733

    
1734
    state.set(FIELD_ADJUSTED_EXTENT, adjustedExtent);
1735
    state.set(FIELD_BACK_COLOR, backColor);
1736
    state.set(FIELD_CLIP, cliprect);
1737
    state.set(FIELD_DIST1PIXEL, dist1pixel);
1738
    state.set(FIELD_DIST3PIXEL, dist3pixel);
1739
    state.set(FIELD_DISTANCE_UNITS, distanceUnits);
1740
    state.set(FIELD_DISTANCE_AREA, distanceArea);
1741

    
1742
    state.set(FIELD_EXTENT, extent);
1743
    state.set(FIELD_EXTENTS, extentsHistory);
1744

    
1745
    state.set(FIELD_MAP_UNITS, mapUnits);
1746
    state.set(FIELD_OFFSET, offset);
1747

    
1748
    state.set(FIELD_PROJ, proj);
1749

    
1750
    state.set(FIELD_IMAGE_SIZE, imageSize);
1751
    
1752
    state.set("selectionColor", this.getSelectionColor());
1753

    
1754
  }
1755

    
1756
  public void loadFromState(PersistentState state) throws PersistenceException {
1757

    
1758
    adjustedExtent = (Rectangle2D) state.get(FIELD_ADJUSTED_EXTENT);
1759
    backColor = (Color) state.get(FIELD_BACK_COLOR);
1760
    cliprect = (Rectangle2D) state.get(FIELD_CLIP);
1761
    dist1pixel = state.getDouble(FIELD_DIST1PIXEL);
1762
    dist3pixel = state.getDouble(FIELD_DIST3PIXEL);
1763
    distanceUnits = state.getInt(FIELD_DISTANCE_UNITS);
1764
    extentsHistory = (ExtentHistory) state.get(FIELD_EXTENTS);
1765
    extent = (Rectangle2D) state.get(FIELD_EXTENT);
1766
    mapUnits = state.getInt(FIELD_MAP_UNITS);
1767
    offset = (Point2D) state.get(FIELD_OFFSET);
1768
    proj = (IProjection) state.get(FIELD_PROJ);
1769
    imageSize = (Dimension) state.get(FIELD_IMAGE_SIZE);
1770
    distanceArea = state.getInt(FIELD_DISTANCE_AREA);
1771

    
1772
    this.selectionColor = (Color) state.get("selectionColor");
1773
    if (this.selectionColor == null) {
1774
        this.selectionColor = DEFAULT_SELECTION_COLOR;
1775
    }
1776
    
1777
    refreshExtent();
1778
  }
1779

    
1780
  public static class RegisterPersistence implements Callable {
1781

    
1782
    public Object call() throws Exception {
1783
      PersistenceManager manager = ToolsLocator.getPersistenceManager();
1784
      if (manager.getDefinition("ViewPort") == null) {
1785
        DynStruct definition = manager.addDefinition(ViewPort.class,
1786
            "ViewPort", "ViewPort Persistence definition", null, null);
1787

    
1788
        definition.addDynFieldObject(FIELD_ADJUSTED_EXTENT)
1789
            .setClassOfValue(Rectangle2D.class).setMandatory(false);
1790

    
1791
        definition.addDynFieldObject(FIELD_BACK_COLOR)
1792
            .setClassOfValue(Color.class).setMandatory(false);
1793

    
1794
        definition.addDynFieldObject(FIELD_CLIP)
1795
            .setClassOfValue(Rectangle2D.class).setMandatory(false);
1796

    
1797
        definition.addDynFieldDouble(FIELD_DIST1PIXEL).setMandatory(true);
1798

    
1799
        definition.addDynFieldDouble(FIELD_DIST3PIXEL).setMandatory(true);
1800

    
1801
        definition.addDynFieldInt(FIELD_DISTANCE_UNITS).setMandatory(true);
1802

    
1803
        definition.addDynFieldInt(FIELD_DISTANCE_AREA).setMandatory(false);
1804

    
1805
        definition.addDynFieldObject(FIELD_EXTENT)
1806
            .setClassOfValue(Rectangle2D.class).setMandatory(false);
1807

    
1808
        definition.addDynFieldObject(FIELD_EXTENTS)
1809
            .setClassOfValue(ExtentHistory.class).setMandatory(true);
1810

    
1811
        definition.addDynFieldInt(FIELD_MAP_UNITS).setMandatory(true);
1812

    
1813
        definition.addDynFieldObject(FIELD_OFFSET)
1814
            .setClassOfValue(Point2D.class).setMandatory(false);
1815

    
1816
        definition.addDynFieldObject(FIELD_PROJ)
1817
            .setClassOfValue(IProjection.class).setMandatory(true);
1818

    
1819
        definition.addDynFieldObject(FIELD_IMAGE_SIZE)
1820
            .setClassOfValue(Dimension.class).setMandatory(false);
1821

    
1822
        definition.addDynFieldObject("selectionColor")
1823
            .setClassOfValue(Color.class)
1824
            .setMandatory(false);
1825

    
1826
      }
1827
      return Boolean.TRUE;
1828
    }
1829

    
1830
  }
1831

    
1832
  /**
1833
   * Clone the view port without clone the listeners nor the extent history.
1834
   *
1835
   * @return the cloned view port
1836
   */
1837
  public Object clone() throws CloneNotSupportedException {
1838

    
1839
    ViewPort clonedViewPort = (ViewPort) super.clone();
1840
    clonedViewPort.listeners = new ArrayList<>();
1841
    clonedViewPort.extentsHistory = new ExtentHistory();
1842

    
1843
    if (this.adjustedExtent != null) {
1844
      clonedViewPort.adjustedExtent = (Rectangle2D) this.adjustedExtent.clone();
1845
    }
1846

    
1847
    if (this.cliprect != null) {
1848
      clonedViewPort.cliprect = (Rectangle2D) this.cliprect.clone();
1849
    }
1850

    
1851
    if (this.extent != null) {
1852
      clonedViewPort.extent = (Rectangle2D) this.extent.clone();
1853
    }
1854
    if (this.imageSize != null) {
1855
      clonedViewPort.imageSize = (Dimension) this.imageSize.clone();
1856
    }
1857

    
1858
    if (this.offset != null) {
1859
      clonedViewPort.offset = (Point2D) this.offset.clone();
1860
    }
1861
    if (proj != null) {
1862
      clonedViewPort.proj = (IProjection) this.proj.clone();
1863
    }
1864

    
1865
    clonedViewPort.trans = (AffineTransform) this.trans.clone();
1866
    
1867
    if (this.selectionColor != null) {
1868
        clonedViewPort.setSelectionColor(this.selectionColor);
1869
    }
1870

    
1871
    if (this.backColor != null) {
1872
        clonedViewPort.setBackColor(this.backColor);
1873
    }
1874

    
1875
    return clonedViewPort;
1876
  }
1877

    
1878
  /**
1879
   * <p>
1880
   * Returns a <code>String</code> representation of the main values of this
1881
   * view port: <code>{@linkplain #extent}</code>,
1882
   * <code>{@linkplain #adjustedExtent}</code>,
1883
   * <code>{@linkplain #imageSize}</code>, <code>{@linkplain #scale}</code>, and
1884
   * <code>{@linkplain #trans}</code>.
1885
   * </p>
1886
   *
1887
   * @return a <code>string</code> representation of the main values of this
1888
   *         view port
1889
   */
1890
  public String toString() {
1891

    
1892
    String str;
1893
    str = "Datos del viewPort:\nExtent=" + extent + "\nadjustedExtent="
1894
        + adjustedExtent + "\nimageSize=" + imageSize + "\nescale=" + scale
1895
        + "\ntrans=" + trans;
1896

    
1897
    return str;
1898
  }
1899

    
1900
  /**
1901
   * <p>
1902
   * Sets the position and size of the clipping rectangle.
1903
   * </p>
1904
   *
1905
   * @param rectView the clipping rectangle to set
1906
   */
1907
  public void setClipRect(Rectangle2D rectView) {
1908
    this.updateDrawVersion();
1909
    cliprect = rectView;
1910
  }
1911

    
1912
  /**
1913
   * <p>
1914
   * Converts and returns the {@link Rectangle2D Rectangle2D}, that is in <i>map
1915
   * coordinates</i> to <i>screen coordinates</i> (pixels) using an <i>inverse
1916
   * transform</i> with the transformation affine information in the
1917
   * {@link #trans #trans} attribute.
1918
   * </p>
1919
   *
1920
   * @param r the 2D rectangle in <i>map coordinates</i>
1921
   * @return 2D rectangle equivalent in <i>screen coordinates</i> (pixels)
1922
   * @see #toMapRectangle(Rectangle2D)
1923
   * @see #fromMapDistance(double)
1924
   * @see #fromMapPoint(Point2D)
1925
   */
1926
  public Rectangle2D fromMapRectangle(Rectangle2D r) {
1927
    Rectangle2D rect = new Rectangle2D.Double();
1928
    Point2D p1 = fromMapPoint((int) r.getX(), (int) r.getY());
1929
    Point2D p2 = fromMapPoint((int) r.getMaxX(), (int) r.getMaxY());
1930
    rect.setFrameFromDiagonal(p1, p2);
1931
    return rect;
1932
  }
1933

    
1934
  /**
1935
   * <p>
1936
   * Recalculates the current <code>{@linkplain #extent}</code> using an scale.
1937
   * It's necessary execute {@linkplain #refreshExtent()} after.
1938
   * </p>
1939
   *
1940
   * @param s the scale to set
1941
   * @deprecated since 07/09/07, use {@linkplain MapContext#setScaleView(long)}
1942
   */
1943
  public void setScale(long s) {
1944
    double x = extent.getX();
1945
    double y = extent.getY();
1946
    double escalaX = imageSize.width / extent.getWidth();
1947
    // double w = imageSize.width / s;
1948
    // double h = imageSize.height / s;
1949
    double difw = escalaX / s;
1950

    
1951
    double x1 = (-x * difw) - x + extent.getWidth() / 2;
1952
    double y1 = (-y * difw) - y + extent.getHeight() / 2;
1953
    double w1 = extent.getWidth() * difw;
1954
    double h1 = extent.getHeight() * difw;
1955
    extent.setRect(-x1, -y1, w1, h1);
1956
  }
1957

    
1958
  public long getDrawVersion() {
1959
    return this.drawVersion;
1960
  }
1961

    
1962
  protected void updateDrawVersion() {
1963
    this.drawVersion++;
1964
  }
1965

    
1966
  public Time getTime() {
1967
    return time;
1968
  }
1969

    
1970
  public void setTime(Time time) {
1971
    this.time = time;
1972
    this.updateDrawVersion();
1973
    callTimeChanged(time);
1974
  }
1975

    
1976
  public double getDPI() {
1977
    if (this.dpi == null) {
1978
      return CompatLocator.getGraphicsUtils().getScreenDPI();
1979
    }
1980
    return this.dpi.doubleValue();
1981
  }
1982

    
1983
  public void setDPI(double dpi) {
1984
    this.dpi = new Double(dpi);
1985
  }
1986

    
1987
  public void setDPIToScreenDPI() {
1988
    this.dpi = null;
1989
  }
1990
}