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 / MapContext.java @ 46505

History | View | Annotate | Download (63.7 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 modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.mapcontext;
24

    
25
import java.awt.Color;
26
import java.awt.Graphics;
27
import java.awt.Graphics2D;
28
import java.awt.geom.Rectangle2D;
29
import java.awt.image.BufferedImage;
30
import java.util.ArrayList;
31
import java.util.Collection;
32
import java.util.HashSet;
33
import java.util.Iterator;
34
import java.util.LinkedHashMap;
35
import java.util.List;
36
import java.util.Map;
37
import java.util.Set;
38
import org.apache.commons.lang3.ArrayUtils;
39

    
40
import org.cresques.cts.ICoordTrans;
41
import org.cresques.cts.IProjection;
42
import org.cresques.geo.Projected;
43
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
45

    
46
import org.gvsig.compat.CompatLocator;
47
import org.gvsig.compat.print.PrintAttributes;
48
import org.gvsig.fmap.dal.DataStore;
49
import org.gvsig.fmap.dal.exception.ReadException;
50
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
51
import org.gvsig.fmap.geom.Geometry;
52
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
53
import org.gvsig.fmap.geom.GeometryLocator;
54
import org.gvsig.fmap.geom.GeometryManager;
55
import org.gvsig.fmap.geom.GeometryUtils;
56
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
57
import org.gvsig.fmap.geom.primitive.Envelope;
58
import org.gvsig.fmap.geom.primitive.Point;
59
import org.gvsig.fmap.mapcontext.events.ErrorEvent;
60
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
61
import org.gvsig.fmap.mapcontext.events.listeners.ErrorListener;
62
import org.gvsig.fmap.mapcontext.events.listeners.EventBuffer;
63
import org.gvsig.fmap.mapcontext.exceptions.LoadLayerException;
64
import org.gvsig.fmap.mapcontext.impl.DefaultMapContextManager;
65
import org.gvsig.fmap.mapcontext.layers.BaseLayerCollectionListener;
66
import org.gvsig.fmap.mapcontext.layers.FLayer;
67
import org.gvsig.fmap.mapcontext.layers.FLayers;
68
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
69
import org.gvsig.fmap.mapcontext.layers.LayerCollectionListener;
70
import org.gvsig.fmap.mapcontext.layers.LayerDrawEvent;
71
import org.gvsig.fmap.mapcontext.layers.LayerDrawingListener;
72
import org.gvsig.fmap.mapcontext.layers.LayerPositionEvent;
73
import org.gvsig.fmap.mapcontext.layers.operations.Classifiable;
74
import org.gvsig.fmap.mapcontext.layers.operations.SingleLayer;
75
import org.gvsig.fmap.mapcontext.layers.order.LayerOrderManager;
76
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
77
import org.gvsig.fmap.mapcontext.layers.vectorial.GraphicLayer;
78
import org.gvsig.fmap.mapcontext.layers.vectorial.VectorLayer;
79
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
80
import org.gvsig.fmap.mapcontext.rendering.strategies.SelectedEnvelopeVisitor;
81
import org.gvsig.timesupport.AbsoluteInstant;
82
import org.gvsig.timesupport.AbsoluteIntervalTypeNotRegisteredException;
83
import org.gvsig.timesupport.Instant;
84
import org.gvsig.timesupport.Interval;
85
import org.gvsig.timesupport.RelativeInstant;
86
import org.gvsig.timesupport.TimeSupportLocator;
87
import org.gvsig.timesupport.TimeSupportManager;
88
import org.gvsig.tools.ToolsLocator;
89
import org.gvsig.tools.dispose.impl.AbstractDisposable;
90
import org.gvsig.tools.dynobject.DynStruct;
91
import org.gvsig.tools.exception.BaseException;
92
import org.gvsig.tools.observer.Observable;
93
import org.gvsig.tools.observer.Observer;
94
import org.gvsig.tools.persistence.PersistenceManager;
95
import org.gvsig.tools.persistence.Persistent;
96
import org.gvsig.tools.persistence.PersistentState;
97
import org.gvsig.tools.persistence.exception.PersistenceException;
98
import org.gvsig.tools.task.Cancellable;
99
import org.gvsig.tools.util.Callable;
100
import org.gvsig.tools.visitor.Visitor;
101

    
102
/**
103
 * <p>
104
 * The <code>MapContext</code> class represents the model and a part of the
105
 * control and view around graphical layers used by
106
 * {@link MapControl MapControl}.</p>
107
 *
108
 * <p>
109
 * An instance of <code>MapContext</code> is made up with:
110
 * <ul>
111
 * <li>a hierarchy of {@link FLayers FLayers} nodes
112
 * <li>a {@link GraphicLayer GraphicLayer} layer
113
 * <li>a {@link ViewPort ViewPort}
114
 * <li>an {@link EventBuffer EventButter}
115
 * <li>some
116
 * {@link com.iver.cit.gvsig.fmap.layers.LegendListener LegendListener}s
117
 * <li>some {@link LayerDrawingListener LayerDrawingListener}s
118
 * <li>some {@link ErrorListener ErrorListener}s
119
 * </ul>
120
 * </p>
121
 */
122
public class MapContext extends AbstractDisposable implements Projected,
123
        Persistent, Observer, Iterable<FLayer> {
124

    
125
    public interface MapTimeContext {
126
        public Interval getInterval();
127
        public List<Instant> getTimes();
128
    }    
129

    
130
    /**
131
     * @deprecated use getDistanceTrans2Meter()
132
     */
133
    public static final double[] CHANGEM = {1000, 1, 0.01, 0.001, 1609.344,
134
        0.9144, 0.3048, 0.0254, 1 / 8.983152841195214E-6};
135

    
136
    public static ArrayList AREANAMES = new ArrayList();
137
    public static ArrayList AREAABBR = new ArrayList();
138
    public static ArrayList AREATRANS2METER = new ArrayList();
139

    
140
    public static ArrayList DISTANCENAMES = new ArrayList();
141
    public static ArrayList DISTANCEABBR = new ArrayList();
142
    public static ArrayList DISTANCETRANS2METER = new ArrayList();
143

    
144
    static {
145
        MapContext.addDistanceUnit("Kilometros", "Km", 1000);
146
        MapContext.addDistanceUnit("Metros", "m", 1);
147
        MapContext.addDistanceUnit("Centimetros", "cm", 0.01);
148
        MapContext.addDistanceUnit("Milimetros", "mm", 0.001);
149
        MapContext.addDistanceUnit("Millas", "mi", 1609.344);
150
        MapContext.addDistanceUnit("Yardas", "Ya", 0.9144);
151
        MapContext.addDistanceUnit("Pies", "ft", 0.3048);
152
        MapContext.addDistanceUnit("Pulgadas", "inche", 0.0254);
153
        MapContext.addDistanceUnit("Grados", "?", 1 / 8.983152841195214E-6);
154

    
155
        MapContext.addAreaUnit("Kilometros", "Km", true, 1000);
156
        MapContext.addAreaUnit("Metros", "m", true, 1);
157
        MapContext.addAreaUnit("Centimetros", "cm", true, 0.01);
158
        MapContext.addAreaUnit("Milimetros", "mm", true, 0.001);
159
        MapContext.addAreaUnit("Millas", "mi", true, 1609.344);
160
        MapContext.addAreaUnit("Yardas", "Ya", true, 0.9144);
161
        MapContext.addAreaUnit("Pies", "ft", true, 0.3048);
162
        MapContext.addAreaUnit("Pulgadas", "inche", true, 0.0254);
163
        MapContext.addAreaUnit("Grados", "?", true, 1 / 8.983152841195214E-6);
164

    
165
    }
166

    
167
    private static final GeometryManager GEOM_MANAGER = GeometryLocator.getGeometryManager();
168

    
169
    private static final MapContextManager MAPCONTEXT_MANAGER = MapContextLocator.getMapContextManager();
170

    
171
    private static final Logger LOGGER = LoggerFactory.getLogger(MapContext.class);
172

    
173
    private long scaleToUseWhenEnvelopeCollapse = 10000000;
174

    
175
    /**
176
     * <p>
177
     * Determines the number of frames.</p>
178
     *
179
     * <p>
180
     * Number of updates per second that the timer will invoke repaint this
181
     * component.</p>
182
     */
183
    private static int drawFrameRate = 3;
184

    
185
    /**
186
     * <p>
187
     * Returns the draw frame rate.</p>
188
     *
189
     * <p>
190
     * Draw frame rate is the number of repaints of this <code>MapControl</code>
191
     * instance that timer invokes per second.</p>
192
     *
193
     * @return number of repaints of this <code>MapControl</code> instance that
194
     * timer invokes per second
195
     *
196
     * @see #applyFrameRate()
197
     * @see #setDrawFrameRate(int)
198
     */
199
    public static int getDrawFrameRate() {
200
        return drawFrameRate;
201
    }
202

    
203
    /**
204
     * <p>
205
     * Sets the draw frame rate.</p>
206
     *
207
     * <p>
208
     * Draw frame rate is the number of repaints of this <code>MapControl</code>
209
     * instance that timer invokes per second.</p>
210
     *
211
     * @param theDrawFrameRate number of repaints of this <code>MapControl</code>
212
     * instance that timer invokes per second
213
     *
214
     * @see #applyFrameRate()
215
     * @see #getDrawFrameRate()
216
     */
217
    public static void setDrawFrameRate(int theDrawFrameRate) {
218
        drawFrameRate = theDrawFrameRate;
219
    }
220

    
221
    public static void addAreaUnit(String name, String abbr, boolean isLinear, double trans2meter) {
222
        if (!AREANAMES.contains(name)) {
223
            AREANAMES.add(name);
224
            String pow = "";
225
            if (isLinear) {
226
                pow = String.valueOf((char) 178);
227
            }
228
            AREAABBR.add(abbr + pow);
229
            AREATRANS2METER.add(trans2meter);
230
        }
231
    }
232

    
233
    public static String[] getAreaNames() {
234
        return (String[]) AREANAMES.toArray(new String[0]);
235
    }
236

    
237
    public static String[] getAreaAbbr() {
238
        return (String[]) AREAABBR.toArray(new String[0]);
239
    }
240

    
241
    public static double[] getAreaTrans2Meter() {
242
        int size = AREATRANS2METER.size();
243
        double[] trans2meters = new double[size];
244
        for (int i = 0; i < size; i++) {
245
            trans2meters[i] = ((Double) AREATRANS2METER.get(i));
246
        }
247
        return trans2meters;
248
    }
249

    
250
    public static String getOfLinear(int i) {
251
        if (((String) AREAABBR.get(i)).toLowerCase().endsWith(String.valueOf((char) 178))) {
252
            return String.valueOf((char) 178);
253
        }
254
        return "";
255
    }
256

    
257
    public static void addDistanceUnit(String name, String abbr, double trans2meter) {
258
        if (!DISTANCENAMES.contains(name)) {
259
            DISTANCENAMES.add(name);
260
            DISTANCEABBR.add(abbr);
261
            DISTANCETRANS2METER.add(trans2meter);
262
        }
263
    }
264

    
265
    public static String[] getDistanceNames() {
266
        return (String[]) DISTANCENAMES.toArray(new String[0]);
267
    }
268

    
269
    public String getDistanceName() {
270
        return (String) DISTANCENAMES.get(this.getViewPort().getDistanceUnits());
271
    }
272

    
273
    public static String[] getDistanceAbbr() {
274
        return (String[]) DISTANCEABBR.toArray(new String[0]);
275
    }
276

    
277
    public static double[] getDistanceTrans2Meter() {
278
        int size = DISTANCETRANS2METER.size();
279
        double[] trans2meters = new double[size];
280
        for (int i = 0; i < size; i++) {
281
            trans2meters[i] = ((Double) DISTANCETRANS2METER.get(i));
282
        }
283
        return trans2meters;
284
    }
285

    
286
    public static int getDistancePosition(String s) {
287
        for (int i = 0; i < DISTANCENAMES.size(); i++) {
288
            if (DISTANCENAMES.get(i).equals(s)) {
289
                return i;
290
            }
291
        }
292
        return 0;
293
    }
294

    
295
    /**
296
     * <p>
297
     * Defines the value which a unit of a distance measurement must be divided
298
     * to obtain its equivalent <b>in centimeters</b>.</p>
299
     *
300
     * <p>
301
     * <b><i>Conversion values of distance measurements:</i></b>
302
     * <ul>
303
     * <li><code>MapContext.CHANGE[0]</code>: kilometer
304
     * <li><code>MapContext.CHANGE[1]</code>: meter
305
     * <li><code>MapContext.CHANGE[2]</code>: centimeter
306
     * <li><code>MapContext.CHANGE[3]</code>: millimeter
307
     * <li><code>MapContext.CHANGE[4]</code>: international statute mile
308
     * <li><code>MapContext.CHANGE[5]</code>: yard
309
     * <li><code>MapContext.CHANGE[6]</code>: foot
310
     * <li><code>MapContext.CHANGE[7]</code>: inch
311
     * <li><code>MapContext.CHANGE[8]</code>: grade
312
     * </ul>
313
     *
314
     * <p>
315
     * <h3>Examples:</h3>
316
     * <pre>1 international statute mile / MapContext.CHANGE[4] = X centimeters</pre>
317
     * <pre>1 kilometer / MapContext.CHANGE[0] = X centimeters</pre>
318
     * <pre>1 grade / MapContext.CHANGE[8] = X centimeters</pre>
319
     * </p>
320
     *
321
     * <p>
322
     * <h3>Grade conversion value: <code>MapContext.CHANGE[8]</code></h3>
323
     * The value of <code>MapContext.CHANGE[8]</code> represents the centimeters
324
     * of a straight line between two points on the Earth surface that are 1
325
     * grade far each other of the center of the Earth. This value has been
326
     * calculated using a radius approximated of
327
     * R<sub>Earth</sub>=6.37846082678100774672e6 meters, according these
328
     * equations:
329
     * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
330
     * <pre>MapContext.CHANGE[8] = 1 / D</pre>
331
     * <h4>Explanation:</h4>
332
     * We get an isosceles triangle with the center of the Earth and the 2
333
     * points on the surface. This triangle can be divided into two rectangle
334
     * triangles. We know two values, the angle of 1 grade, that will be 0.50
335
     * grades in each triangle, and the Earth radius that is the hypotenuse.
336
     * Then we apply trigonometry and get the distance <i>D</i> between both
337
     * points on the Earth surface.</p>
338
     * <p>
339
     * Now we only must invert that value to obtain
340
     * <code>MapContext.CHANGE[8]</code>.</p>
341
     *
342
     * @deprecated use getDistanceTrans2Meter() * 100
343
     */
344
    public static final double[] CHANGE = {100000, 100, 1, 0.1, 160934.4,
345
        91.44, 30.48, 2.54, 1 / 8.983152841195214E-4};
346

    
347
    public static final int EQUALS = 0;
348
    public static final int DISJOINT = 1;
349
    public static final int INTERSECTS = 2;
350
    public static final int TOUCHES = 3;
351
    public static final int CROSSES = 4;
352
    public static final int WITHIN = 5;
353
    public static final int CONTAINS = 6;
354
    public static final int OVERLAPS = 7;
355

    
356
    /**
357
     * A hierarchy of {@link FLayers FLayers} nodes.
358
     */
359
    protected FLayers layers;
360

    
361
    /**
362
     * A List of layers with graphical items: geometries and symbols.
363
     */
364
    private static final String DEFAULT_TRACTLAYER = "Default";
365
    private Map<String, VectorLayer> tracLayers;
366

    
367
    /**
368
     * Information for draw layers in a view.
369
     *
370
     * @see #getViewPort()
371
     * @see #setViewPort(ViewPort)
372
     */
373
    private ViewPort viewPort;
374
    // private ArrayList invalidationListeners = new ArrayList();
375
    /**
376
     * Array list with all {@link LegendListener LegendListener} registered to
377
     * this map.
378
     *
379
     * @see #addLayerListener(LegendListener)
380
     * @see #removeLayerListener(LegendListener)
381
     * @see #callLegendChanged()
382
     */
383
    private List legendListeners;
384

    
385
    /**
386
     * Array list with all {@link LayerDrawingListener LayerDrawingListener}
387
     * registered to this map.
388
     *
389
     * @see #addLayerDrawingListener(LayerDrawingListener)
390
     * @see #removeLayerDrawListener(LayerDrawingListener)
391
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
392
     */
393
    private ArrayList layerDrawingListeners = new ArrayList();
394

    
395
    /**
396
     * <p>
397
     * Buffer that is used to store and eject events produced on this map:
398
     * <ul>
399
     * <li>Layer collection events.
400
     * <li>View port events.
401
     * <li>Atomic events.
402
     * <li>Layer events.
403
     * <li>Legend events on a {@link Classificable Classificable} layer.
404
     * <li>Selection events on an {@link AlphanumericData AlphanumericData} data
405
     * layer.
406
     * </ul>
407
     * </p>
408
     *
409
     * @see #addAtomicEventListener(AtomicEventListener)
410
     * @see #removeAtomicEventListener(AtomicEventListener)
411
     * @see #beginAtomicEvent()
412
     * @see #endAtomicEvent()
413
     */
414
    private EventBuffer eventBuffer = new EventBuffer();
415

    
416
    /**
417
     * Event listener for the collection of layers of this map.
418
     */
419
    private LayerEventListener layerEventListener = null;
420

    
421
    /**
422
     * List with information of all errors produced on all layers.
423
     *
424
     * @see #addLayerError(String)
425
     * @see #getLayersError()
426
     * @see #clearErrors()
427
     */
428
    private ArrayList layersError = new ArrayList();
429

    
430
    /**
431
     * Array list with all {@link ErrorListener ErrorListener} registered to
432
     * this map.
433
     *
434
     * @see #addErrorListener(ErrorListener)
435
     * @see #removeErrorListener(LegendListener)
436
     * @see #reportDriverExceptions(String, List)
437
     */
438
    private ArrayList errorListeners = new ArrayList();
439

    
440
    /**
441
     * Layer order manager decides position of layers added to this map context.
442
     */
443
    private LayerOrderManager orderManager = null;
444

    
445
        // public static ResourceBundle myResourceBundle =
446
    // ResourceBundle.getBundle("FMap");
447
    /**
448
     * <p>
449
     * Default <i>zoom in</i> factor.</p>
450
     * <p>
451
     * Doing a <i>zoom in</i> operation, decreases the focal distance and
452
     * increases the eyesight angle to the surface. This allows view an smaller
453
     * area but with the items bigger.</p>
454
     */
455
    public static double ZOOMINFACTOR = 2;
456

    
457
    /**
458
     * <p>
459
     * Default <i>zoom out</i> factor.</p>
460
     * <p>
461
     * Doing a <i>zoom out</i> operation, increases the focal distance and
462
     * decreases the eyesight angle to the surface. This allows view a bigger
463
     * area but with the items smaller.</p>
464
     */
465
    public static double ZOOMOUTFACTOR = 0.5;
466

    
467
    /**
468
     *          * Draw version of the context. It's used for know when de componend has
469
     * changed any visualization property
470
     *
471
     * @see getDrawVersion
472
     * @see updateDrawVersion
473
     */
474
    private long drawVersion = 0L;
475

    
476
    private long layersVersion = 0L;
477
    private long viewPortVersion = 0L;
478
    private long graphicsLayerVersion = 0L;
479

    
480
    /**
481
     * Object to Manage Draw of MapContext
482
     */
483
    private MapContextDrawer mapContextDrawer = null;
484

    
485
    /**
486
     * Object to Manage Draw of MapContext
487
     */
488
    private Class mapContextDrawerClass = null;
489

    
490
    /**
491
     * <p>
492
     * Color used to represent the selections.</p>
493
     */
494
    private static Color selectionColor = Color.YELLOW;
495
    private Set<FLyrVect> layersToSnap = new HashSet<>();
496

    
497
    /**
498
     * <p>
499
     * Gets the color used to represent the selections.</p>
500
     *
501
     * @return color used to represent the selections
502
     */
503
    public static Color getSelectionColor() {
504
        return selectionColor;
505
    }
506

    
507
    /**
508
     * <p>
509
     * Sets the color used to represent the selections.</p>
510
     *
511
     * @param selectionColor color used to represent the selections
512
     */
513
    public static void setSelectionColor(Color selectionColor) {
514
        MapContext.selectionColor = selectionColor;
515
    }
516

    
517
    /**
518
     * <p>
519
     * Creates a new map context with the drawing information defined in the
520
     * view port argument, and without layers.</p>
521
     *
522
     * @param vp information for drawing the layers of this map in the available
523
     * rectangular area according a projection
524
     */
525
    public MapContext(ViewPort vp) {
526
        this(new FLayers(), vp);
527
    }
528

    
529
    public MapContext() {
530
        this.legendListeners = new ArrayList();
531
        this.tracLayers = new LinkedHashMap<>();
532
        this.layerEventListener = new LayerEventListener();
533
    }
534

    
535
    /**
536
     * <p>
537
     * Creates a new map context with the layers and the drawing information
538
     * defined in the view port arguments.</p>
539
     *
540
     * @param fLayers the initial hierarchy of nodes of layers that this map
541
     * will have
542
     * @param vp information for drawing the layers of this map in the available
543
     * rectangular area according a projection
544
     */
545
    public MapContext(FLayers fLayers, ViewPort vp) {
546
        this();
547
        this.layers = fLayers;
548
        if (layers != null) {
549
            layers.setMapContext(this);
550
            layers.addLayerCollectionListener(layerEventListener);
551
            layers.addLayerCollectionListener(eventBuffer);
552
        }
553
        setViewPort(vp);
554
    }
555

    
556
    /**
557
     * <p>
558
     * Reports to all driver error listeners registered of a bundle of driver
559
     * exceptions caused in the same map atomic transaction.</p>
560
     *
561
     * @param introductoryText introductory text specified by developer. If
562
     * <code>null</code>, use ""
563
     * @param driverExceptions list with a bundle of driver exceptions caught
564
     * during an atomic event
565
     *
566
     * @see #addErrorListener(ErrorListener)
567
     * @see #removeErrorListener(LegendListener)
568
     */
569
    public synchronized void reportDriverExceptions(String introductoryText,
570
            List driverExceptions) {
571
        for (int i = 0; i < errorListeners.size(); i++) {
572
            ((ErrorListener) errorListeners.get(i)).
573
                    reportDriverExceptions(introductoryText, driverExceptions);
574
        }
575
    }
576

    
577
    /**
578
     * <p>
579
     * Adds the specified legend listener (if didn't exist) to receive legend
580
     * events from this map.</p>
581
     *
582
     * @param listener the legend listener
583
     *
584
     * @see #removeLayerListener(LegendListener)
585
     * @see #callLegendChanged()
586
     */
587
    public void addLayerListener(LegendListener listener) {
588
        if (!legendListeners.contains(listener)) {
589
            legendListeners.add(listener);
590
        }
591
    }
592

    
593
    /**
594
     * <p>
595
     * Adds the specified layer drawing listener to catch and handle drawing
596
     * events from layers of this map.</p>
597
     *
598
     * @param listener the listener to add
599
     *
600
     * @see #removeLayerDrawListener(LayerDrawingListener)
601
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
602
     */
603
    public void addLayerDrawingListener(LayerDrawingListener listener) {
604
        layerDrawingListeners.add(listener);
605
    }
606

    
607
    /**
608
     * <p>
609
     * Removes the specified layer drawing listener from this map.</p>
610
     *
611
     * @param listener the listener to remove
612
     *
613
     * @see #addLayerDrawingListener(LayerDrawingListener)
614
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
615
     */
616
    public void removeLayerDrawListener(LayerDrawingListener listener) {
617
        layerDrawingListeners.remove(listener);
618
    }
619

    
620
    /**
621
     * <p>
622
     * Adds the specified error listener to receive error events from this
623
     * map.</p>
624
     *
625
     * @param listener the listener to add
626
     *
627
     * @see #removeErrorListener(LegendListener)
628
     * @see #reportDriverExceptions(String, List)
629
     */
630
    public void addErrorListener(ErrorListener listener) {
631
        errorListeners.add(listener);
632
    }
633

    
634
    /**
635
     * <p>
636
     * Removes the specified error listener from this map.</p>
637
     *
638
     * @param listener the listener to remove
639
     *
640
     * @see #addErrorListener(ErrorListener)
641
     * @see #reportDriverExceptions(String, List)
642
     */
643
    public void removeErrorListener(LegendListener listener) {
644
        legendListeners.remove(listener);
645
    }
646

    
647
    /**
648
     * Notifies to all legend listeners registered, that one legend has
649
     * changed.
650
     * 
651
     * This method must be called only if it's wanted to reflect a legend
652
     * change.
653
     *
654
     * @see #addLayerListener(LegendListener)
655
     * @see #removeLayerListener(LegendListener)
656
     */
657
    public synchronized void callLegendChanged() {
658
        for (int i = 0; i < legendListeners.size(); i++) {
659
            ((LegendListener) legendListeners.get(i)).legendChanged(null);
660
        }
661
        // getLayers().moveTo(0,0);
662
    }
663

    
664
    /**
665
     * <p>
666
     * Fires a layer drawing event to all
667
     * {@link LayerDrawingListener LayerDrawingListener} listeners registered,
668
     * distinguishing the kind of event.</p>
669
     *
670
     * @param e the event
671
     *
672
     * @see #addLayerDrawingListener(LayerDrawingListener)
673
     * @see #removeLayerDrawListener(LayerDrawingListener)
674
     */
675
    public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
676
        for (int i = 0; i < layerDrawingListeners.size(); i++) {
677
            LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
678
            switch (e.getEventType()) {
679
                case LayerDrawEvent.LAYER_BEFORE_DRAW:
680
                    listener.beforeLayerDraw(e);
681
                    break;
682
                case LayerDrawEvent.LAYER_AFTER_DRAW:
683
                    listener.afterLayerDraw(e);
684
                    break;
685
                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
686
                    listener.beforeGraphicLayerDraw(e);
687
                    break;
688
                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
689
                    listener.afterLayerGraphicDraw(e);
690
                    break;
691
            }
692
        }
693
        // getLayers().moveTo(0,0);
694
    }
695

    
696
    /**
697
     * <p>
698
     * Notifies to all error listeners registered, that one error has been
699
     * produced.</p>
700
     *
701
     * @param e the event with information of the error
702
     *
703
     * @see #addErrorListener(ErrorListener)
704
     * @see #removeErrorListener(LegendListener)
705
     * @see #reportDriverExceptions(String, List)
706
     */
707
    public synchronized void callNewErrorEvent(ErrorEvent e) {
708
        for (int i = 0; i < errorListeners.size(); i++) {
709
            ((ErrorListener) errorListeners.get(i)).errorThrown(e);
710
        }
711
        errorListeners.clear();
712
        // getLayers().moveTo(0,0);
713
    }
714

    
715
    /**
716
     * <p>
717
     * Removes the specified layer listener from this map.</p>
718
     *
719
     * @param listener the listener to remove
720
     *
721
     * @see #addLayerListener(LegendListener)
722
     * @see #callLegendChanged()
723
     */
724
    public void removeLayerListener(LegendListener listener) {
725
        legendListeners.remove(listener);
726
    }
727

    
728
    /**
729
     * <p>
730
     * Returns the hierarchy of {@link FLayers FLayers} nodes stored in this
731
     * map.</p>
732
     *
733
     * @return the hierarchy of nodes of layers stored in this map
734
     */
735
    public FLayers getLayers() {
736
        return layers;
737
    }
738

    
739
    /**
740
     * <p>
741
     * Draws the visible layers of this map according its view port, on the
742
     * image parameter.</p>
743
     *
744
     * @param b image with an accessible buffer of image data
745
     */
746
    public void drawLabels(BufferedImage b) {
747
    }
748

    
749
    /**
750
     * @see #redraw()
751
     */
752
    public void invalidate() {
753
        updateDrawVersion();
754
        this.viewPort.updateDrawVersion();
755
        // Small hack to let the MapControl receive an event and repaint
756
        FLayer layer;
757
        if (layers.getLayersCount() > 0) {
758
            layer = layers.getLayer(layers.getLayersCount() - 1);
759
        } else {
760
            layer = getGraphicsLayer();
761
        }
762
        LayerPositionEvent event = LayerPositionEvent.createLayerMovedEvent(layer, 0, 0);
763
        eventBuffer.layerMoved(event);
764
    }
765

    
766
    /**
767
     * <p>
768
     * Prints the layers of this map using the {@link Graphics2D Graphics2D}
769
     * argument, that usually is the {@link Graphics Graphics} of the printer.
770
     * </p>
771
     *
772
     * @param g for rendering 2-dimensional shapes, text and images on the
773
     * Java(tm) platform
774
     * @param scale the scale of the view. Must be between
775
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
776
     * @param properties a set with the settings to be applied to a whole print
777
     * job and to all the documents in the print job
778
     * @throws MapContextException if there is an error getting the instance of
779
     * MapContextDrawer
780
     *
781
     * @throws ReadDriverException if fails reading with driver.
782
     *
783
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
784
     * double)
785
     */
786
    public void print(Graphics2D g, double scale,
787
            PrintAttributes properties) throws ReadException,
788
            MapContextException {
789

    
790
        CompatLocator.getGraphicsUtils().setRenderingHintsForPrinting(g);
791

    
792
        Cancellable cancel = new Cancellable() {
793
            public boolean isCanceled() {
794
                return false;
795
            }
796

    
797
            public void setCanceled(boolean canceled) {
798
                // No queremos que se pueda cancelar la impresi?n.
799

    
800
            }
801
        };
802
        this.getMapContextDrawer().print(this.layers, g, cancel, scale, properties);
803
        for (VectorLayer tracLayer : this.tracLayers.values()) {
804
            if (tracLayer != null) {
805
                tracLayer.draw(null, g, viewPort, cancel, scale);
806
            }
807
        }
808
    }
809

    
810
    /**
811
     * <p>
812
     * Returns a new <code>MapContext</code> instance with the information of
813
     * the <code>vp</code> argument, and the layers of this map.</p>
814
     *
815
     * @param vp information for drawing the layers of this map in the available
816
     * rectangular area according a projection
817
     *
818
     * @return a new <code>MapContext</code> instance projected by
819
     * <code>vp</code>
820
     */
821
    public MapContext createNewFMap(ViewPort vp) {
822
        MapContext ret = new MapContext(vp);
823
        ret.layers = this.layers;
824

    
825
        return ret;
826
    }
827

    
828
    /**
829
     * <p>
830
     * Creates a new independent <code>MapContext</code> instance, that has a
831
     * clone of the layers and the view port of this one.</p>
832
     * <p>
833
     * The new map will have the same data source drivers to avoid waste memory,
834
     * and work faster.</p>
835
     *
836
     * @return the new <code>MapContext</code> instance
837
     *
838
     * @throws XMLException if fails cloning the view port or a layer
839
     *
840
     * @see FLayer#cloneLayer()
841
     * @see ViewPort#cloneViewPort()
842
     */
843
    public MapContext cloneFMap() {
844
        ViewPort vp;
845
        try {
846
            vp = (ViewPort) getViewPort().clone();
847
        } catch (CloneNotSupportedException e) {
848
            throw new RuntimeException(e);
849
        }
850
        FLayers antLayers = getLayers();
851
        MapContext ret = new MapContext(vp);
852

    
853
        /*
854
         * Cloning order manager
855
         */
856
        LayerOrderManager lom = this.getOrderManager();
857
        try {
858
            lom = (LayerOrderManager) lom.clone();
859
            ret.setOrderManager(lom);
860
        } catch (CloneNotSupportedException e1) {
861
            LOGGER.error("While cloning order manager", e1);
862
        }
863

    
864
        for (int i = 0; i < antLayers.getLayersCount(); i++) {
865
            FLayer lyr = antLayers.getLayer(i);
866
            try {
867
                FLayer auxLayer = lyr.cloneLayer();
868
                ret.layers.addLayer(auxLayer);
869
                auxLayer.dispose();
870
            } catch (Exception e) {
871
                throw new RuntimeException(e);
872
            }
873
        }
874
        return ret;
875

    
876
    }
877

    
878
    /**
879
     * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather
880
     * copies them.
881
     *
882
     * @return the new map
883
     */
884
    public MapContext cloneToDraw() {
885
        ViewPort vp;
886
        try {
887
            vp = (ViewPort) getViewPort().clone();
888
            MapContext mapContext = new MapContext(getLayers(), vp);
889
            return mapContext;
890
        } catch (CloneNotSupportedException e) {
891
            throw new RuntimeException(e);
892
        }
893
    }
894

    
895
    /**
896
     * <p>
897
     * Adds a layer to the group of layers that are at a upper level in the
898
     * tree.</p>
899
     *
900
     * @param vectorial the layer to add
901
     */
902
    public void addToTrackLayer(FLayer vectorial) {
903
    }
904

    
905
    /**
906
     * <p>
907
     * Returns the scale of the view in the screen.</p>
908
     *
909
     * @return one of this values:
910
     * <ul>
911
     * <li>the scale of the adjusted extent scale of the view in the screen
912
     * <li><code>-1</code> if there is no image
913
     * <li><code>0</code> if there is no extent defined for the image
914
     * </ul>
915
     *
916
     * @see #setScaleView(long)
917
     * @see ViewPort#getAdjustedExtent()
918
     * @see IProjection#getScale(double, double, double, double)
919
     */
920
    public long getScaleView() {
921
        double dpi = this.getViewPort().getDPI();
922
        IProjection proj = viewPort.getProjection();
923

    
924
        if (viewPort.getImageSize() == null) {
925
            return -1;
926
        }
927

    
928
        if (viewPort.getAdjustedEnvelope() == null) {
929
            return 0;
930
        }
931
        double[] trans2Meter = getDistanceTrans2Meter();
932
        int mUnits = getViewPort().getMapUnits();
933

    
934
        if (proj == null) {
935
            double w = ((viewPort.getImageSize().width / dpi) * 0.0254);
936
            return (long) (viewPort.getAdjustedEnvelope().getLength(0)
937
                    / w * trans2Meter[mUnits]);
938
        }
939

    
940
        return Math.round(proj.getScale(
941
                viewPort.getAdjustedEnvelope().getMinimum(0) * trans2Meter[mUnits],
942
                viewPort.getAdjustedEnvelope().getMaximum(0) * trans2Meter[mUnits],
943
                viewPort.getImageSize().width,
944
                dpi));
945

    
946
    }
947

    
948
    /**
949
     * <p>
950
     * Sets the new extent of the view, calculated using the scale argument.</p>
951
     * <p>
952
     * Doesn't updates the scale if there isn't information about the dimension
953
     * of the image or the adjusted extent.</p>
954
     *
955
     * @param scale the new scale for the view
956
     *
957
     * @see ViewPort#setProjection(IProjection)
958
     * @see #getScaleView()
959
     */
960
    public void setScaleView(long scale) {
961
        double dpi = this.getViewPort().getDPI();
962
        if (viewPort.getImageSize() == null) {
963
            return;
964
        }
965
        IProjection proj = viewPort.getProjection();
966
        if (viewPort.getAdjustedExtent() == null) {
967
            return;
968
        }
969
        double[] trans2Meter = getDistanceTrans2Meter();
970
        Envelope env = viewPort.getAdjustedExtent();
971
        Rectangle2D r = new Rectangle2D.Double(env.getMinimum(0), env.getMinimum(1), env.getLength(0), env.getLength(1));
972
        Rectangle2D rec = proj.getExtent(r, scale, viewPort.getImageWidth(), viewPort.getImageHeight(), 100 * getDistanceTrans2Meter()[getViewPort().getMapUnits()], trans2Meter[getViewPort().getDistanceUnits()], dpi);
973
        try {
974
            getViewPort().setEnvelope(GEOM_MANAGER.createEnvelope(rec.getX(), rec.getY(), rec.getMaxX(), rec.getMaxY(), SUBTYPES.GEOM2D));
975
        } catch (CreateEnvelopeException e) {
976
            LOGGER.error("Error seting the bounding box");
977
        }
978
    }
979

    
980
    /**
981
     * <p>
982
     * Returns the screen resolution (Dots Per Inch) as it was defined by the
983
     * user's preference, or by default as it is defined in the default
984
     * Toolkit.</p>
985
     *
986
     * Be care, use ViewPort#getDPI to ensure are using the corrects DPI.
987
     *
988
     * @return double with the screen's dpi
989
     */
990
    public static double getScreenDPI() {
991
        return CompatLocator.getGraphicsUtils().getScreenDPI();
992
    }
993

    
994
    /**
995
     * @see
996
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
997
     */
998
    public void process(Visitor visitor) {
999
    }
1000

    
1001
    /**
1002
     * @see
1003
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
1004
     */
1005
    public void processSelected(Visitor visitor) {
1006
    }
1007

    
1008
    /**
1009
     * @see
1010
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
1011
     * VectorialSubSet)
1012
     */
1013
    public void select(Visitor visitor) {
1014
    }
1015

    
1016
    /**
1017
     * @see
1018
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
1019
     */
1020
    public void selectFromSelection() {
1021
    }
1022

    
1023
    /**
1024
     * @see
1025
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
1026
     */
1027
    public void createIndex() {
1028
    }
1029

    
1030
    /**
1031
     * @see org.cresques.geo.Projected#getProjection()
1032
     *
1033
     * @see ViewPort#getProjection()
1034
     * @see #setProjection(IProjection)
1035
     * @see #reProject(ICoordTrans)
1036
     */
1037
    public IProjection getProjection() {
1038
        return getViewPort().getProjection();
1039
    }
1040

    
1041
    /**
1042
     * <p>
1043
     * Sets the new projection.</p>
1044
     *
1045
     * @param proj the new projection
1046
     *
1047
     * @see #getProjection()
1048
     * @see ViewPort#setProjection(IProjection)
1049
     * @see #reProject(ICoordTrans)
1050
     */
1051
    public void setProjection(IProjection proj) {
1052
        if (getViewPort() != null) {
1053
            getViewPort().setProjection(proj);
1054
        }
1055
    }
1056

    
1057
    /**
1058
     * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
1059
     */
1060
    public void reProject(ICoordTrans arg0) {
1061
        // TODO implementar reprojecci?n (lo que sea eso)
1062
    }
1063

    
1064
    public Envelope getSelectionBounds() throws BaseException {
1065

    
1066
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1067

    
1068
        layers.accept(visitor);
1069
        Envelope env_in_data_crs = visitor.getSelectioEnvelope();
1070
        return env_in_data_crs;
1071
    }
1072

    
1073
    /**
1074
     * <p>
1075
     * Draws this map if its {@link ViewPort ViewPort} has an extent
1076
     * defined:<br>
1077
     * <ol>
1078
     * <li>Selects only the layers that have to be drawn:
1079
     * {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
1080
     * <li>Sets quality: antialiasing by text and images, and quality rendering.
1081
     * <li>Draws the layers.
1082
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
1083
     * <li>Draws the graphic layer.
1084
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
1085
     * <li>Invokes the garbage collector and memory clean.
1086
     * </ol>
1087
     * </p>
1088
     *
1089
     * @param image buffer used sometimes instead <code>g</code> to accelerate
1090
     * the draw. For example, if two points are as closed that can't be
1091
     * distinguished, draws only one.
1092
     * @param g for rendering 2-dimensional shapes, text and images on the
1093
     * Java(tm) platform
1094
     * @param cancel shared object that determines if this layer can continue
1095
     * being drawn
1096
     * @param scale the scale of the view. Must be between
1097
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1098
     * @throws MapContextException if there is an error getting the instance of
1099
     * MapContextDrawer
1100
     * @throws ReadDriverException if fails reading with the driver.
1101
     */
1102
    public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
1103
            double scale) throws ReadException, MapContextException {
1104
        if (viewPort.getEnvelope() == null) {
1105
            return;
1106
        }
1107

    
1108
        CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1109

    
1110
        this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1111
    }
1112

    
1113
    /**
1114
     * <p>
1115
     * Draws only the internal graphic layer using the information of the
1116
     * {@link ViewPort ViewPort} of this map.</p>
1117
     *
1118
     * @param image image used to accelerate the screen draw
1119
     * @param g for rendering 2-dimensional shapes, text and images on the
1120
     * Java(tm) platform
1121
     * @param cancel shared object that determines if this layer can continue
1122
     * being drawn
1123
     * @param scale value that represents the scale
1124
     * @throws ReadDriverException if fails reading with the driver.
1125
     * @deprecated use
1126
     * {@link #draw(BufferedImage, Graphics2D, Cancellable, double)} instead
1127
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
1128
     * double)
1129
     */
1130
    public void drawGraphics(BufferedImage image, Graphics2D g,
1131
            Cancellable cancel, double scale) throws ReadException {
1132

    
1133
                // From now on the graphics layer is handled by the MapContextDrawer,
1134
        // so call the draw method instead.
1135
        try {
1136
            draw(image, g, cancel, scale);
1137
        } catch (MapContextException e) {
1138
            throw new RuntimeException(e);
1139
        }
1140
    }
1141

    
1142
    /**
1143
     * <p>
1144
     * Like
1145
     * {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}
1146
     * , but creating the task as cancellable.
1147
     * </p>
1148
     *
1149
     * @param image buffer used sometimes instead <code>g</code> to accelerate
1150
     * the draw. For example, if two points are as closed that can't be
1151
     * distinguished, draws only one.
1152
     * @param g for rendering 2-dimensional shapes, text and images on the
1153
     * Java(tm) platform
1154
     * @param scale the scale of the view. Must be between
1155
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1156
     * @throws MapContextException if there is an error getting the instance of
1157
     * MapContextDrawer
1158
     *
1159
     * @throws ReadDriverException if the driver fails reading.
1160
     *
1161
     * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1162
     */
1163
    public void draw(BufferedImage image, Graphics2D g, double scale)
1164
            throws ReadException, MapContextException {
1165
        draw(image, g, new Cancellable() {
1166
            /**
1167
             * @see org.gvsig.utils.swing.threads.Cancellable#isCanceled()
1168
             */
1169
            public boolean isCanceled() {
1170
                return false;
1171
            }
1172

    
1173
            public void setCanceled(boolean canceled) {
1174
                // Do nothing
1175
            }
1176
        }, scale);
1177
    }
1178

    
1179
    /**
1180
     * <p>
1181
     * Gets the {@link ViewPort ViewPort} associated to this map.</p>
1182
     *
1183
     * @return the view port
1184
     *
1185
     * @see #setViewPort(ViewPort)
1186
     */
1187
    public ViewPort getViewPort() {
1188
        return viewPort;
1189
    }
1190
    
1191
    /**
1192
     * <p>
1193
     * Sets a {@link ViewPort ViewPort} with the drawing information of this
1194
     * map.</p>
1195
     * <p>
1196
     * If there was a previous view port, removes its
1197
     * {@link EventBuffer EventBuffer} and adds the new one.</p>
1198
     *
1199
     * @param viewPort the viewPort
1200
     *
1201
     * @see #getViewPort()
1202
     */
1203
    public void setViewPort(ViewPort viewPort) {
1204
        if (this.viewPort != null) {
1205
            this.viewPort.removeViewPortListener(eventBuffer);
1206
        }
1207

    
1208
        if (this.mapContextDrawer != null) {
1209
            this.mapContextDrawer.setViewPort(viewPort);
1210
        }
1211

    
1212
        this.viewPort = viewPort;
1213
        if (viewPort != null) {
1214
            viewPort.addViewPortListener(eventBuffer);
1215
        }
1216
    }
1217

    
1218
    /**
1219
     * <p>
1220
     * Sets the given extent to the {@link ViewPort ViewPort} and updates the
1221
     * view with the new zoom.</p>
1222
     *
1223
     * @param extent the extent of the new zoom
1224
     */
1225
    public void zoomToEnvelope(Envelope env) {
1226
        if (env == null || env.isEmpty()) {
1227
            return;
1228
        }
1229
        if( env.isCollapsed(Geometry.SUBTYPES.GEOM2D) ) {
1230
            // El envelop ha colapsado en X e Y.
1231
            // Fijamos la escala por defecto para cuando colapsa y centramos
1232
            // sobre el centro del envelop.
1233
            
1234
            this.setScaleView(this.getScaleToUseWhenEnvelopeCollapse());
1235
            
1236
            Envelope curenv = viewPort.getEnvelope();
1237
            double halfWidth = curenv.getLength(Geometry.DIMENSIONS.X) / 2;
1238
            double halfHeight = curenv.getLength(Geometry.DIMENSIONS.Y) / 2;
1239
            double centerx = env.getCenter(Geometry.DIMENSIONS.X);
1240
            double centery = env.getCenter(Geometry.DIMENSIONS.Y);
1241

    
1242
            Point lowerCorner = GeometryUtils.createPoint(halfWidth + centerx, halfHeight + centery);
1243
            Point upperCorner = GeometryUtils.createPoint(halfWidth - centerx, halfHeight - centery);
1244

    
1245
            env.setLowerCorner(lowerCorner);
1246
            env.setUpperCorner(upperCorner);            
1247
        }
1248
        this.viewPort.setEnvelope(env);
1249
    }
1250

    
1251
    /**
1252
     * <p>
1253
     * Returns the union of all extents of all layers of this map.</p>
1254
     *
1255
     * @return full extent of layers of this map
1256
     * @throws ReadDriverException if the driver fails reading.
1257
     *
1258
     * @see FLayers#getFullEnvelope()
1259
     */
1260
    public Envelope getFullEnvelope() throws ReadException {
1261
        Envelope envelope = layers.getFullEnvelope();
1262

    
1263
        for (VectorLayer tracLayer : this.tracLayers.values()) {
1264
            if (tracLayer != null) {
1265
                Envelope graphicsEnvelope = tracLayer.getFullEnvelope();
1266
                if( graphicsEnvelope==null ) {
1267
                    continue;
1268
                }
1269
                if (envelope == null) {
1270
                    try {
1271
                        envelope =  (Envelope) graphicsEnvelope.clone();
1272
                    } catch (CloneNotSupportedException ex) {
1273
                    }
1274
                } else if (graphicsEnvelope != null) {
1275
                    envelope.add(graphicsEnvelope);
1276
                }
1277
            }
1278
        }
1279
        return envelope;
1280
    }
1281

    
1282
    /**
1283
     * <p>
1284
     * Adds a listener of atomic events to the internal
1285
     * {@link EventBuffer EventBuffer}.</p>
1286
     *
1287
     * @param listener the new listener
1288
     *
1289
     * @return <code>true</code> if has added the listener successfully
1290
     *
1291
     * @see #removeAtomicEventListener(AtomicEventListener)
1292
     * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1293
     */
1294
    public boolean addAtomicEventListener(AtomicEventListener listener) {
1295
        return eventBuffer.addAtomicEventListener(listener);
1296
    }
1297

    
1298
    /**
1299
     * <p>
1300
     * Removes a listener of atomic events from the internal
1301
     * {@link EventBuffer EventBuffer}.</p>
1302
     *
1303
     * @param listener the listener to remove
1304
     *
1305
     * @return <tt>true</tt> if the list contained the specified element
1306
     *
1307
     * @see #addAtomicEventListener(AtomicEventListener)
1308
     * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1309
     */
1310
    public boolean removeAtomicEventListener(AtomicEventListener listener) {
1311
        return eventBuffer.removeAtomicEventListener(listener);
1312
    }
1313

    
1314
    /**
1315
     * @see EventBuffer#beginAtomicEvent()
1316
     *
1317
     * @see #endAtomicEvent()
1318
     */
1319
    public void beginAtomicEvent() {
1320
        eventBuffer.beginAtomicEvent();
1321
    }
1322

    
1323
    /**
1324
     * @see EventBuffer#endAtomicEvent()
1325
     *
1326
     * @see #beginAtomicEvent()
1327
     */
1328
    public void endAtomicEvent() {
1329
        eventBuffer.endAtomicEvent();
1330
    }
1331

    
1332
    /**
1333
     * <p>
1334
     * The class <code>LayerEventListener</code> implements the methods of
1335
     * {@link LayerCollectionListener LayerCollectionListener} that handles the
1336
     * "layer added" or "layer removed" events in a map.</p>
1337
     * <p>
1338
     * Is designed as a listener for all layers in a
1339
     * {@link MapContext MapContext}.</p>
1340
     *
1341
     * @author Fernando Gonz?lez Cort?s
1342
     */
1343
    public class LayerEventListener extends BaseLayerCollectionListener {
1344

    
1345
        @Override
1346
        public void layerAdded(LayerCollectionEvent e) {
1347
            // Voy a mover todo esto a la vista
1348
//            // Si aun no tenemos envelope, problablemente sera la primera capa,
1349
//            // asi que asignamos el envelope de la capa al mapcontext.
1350
//            if (getViewPort().getEnvelope() == null) {
1351
//                FLayer lyr = e.getAffectedLayer();
1352
//                if (lyr.isAvailable()) {
1353
//                    try {
1354
//                        getViewPort().setEnvelope(lyr.getFullEnvelope());
1355
//                    } catch (Exception ex) {
1356
//                        logger.warn(
1357
//                                MessageFormat.format(
1358
//                                    "Can''t set envelope to view port from layer ''{0}''",
1359
//                                    new Object[]{lyr.getName()}
1360
//                                ),
1361
//                                ex
1362
//                        );
1363
//                    }
1364
//                }
1365
//            }
1366

    
1367
            // Registramos al FMap como listener del legend de las capas
1368
            FLayer lyr = e.getAffectedLayer();
1369
            addSelectionListener(lyr);
1370
        }
1371

    
1372
        @Override
1373
        public void layerRemoved(LayerCollectionEvent e) {
1374
            FLayer lyr = e.getAffectedLayer();
1375

    
1376
            lyr.removeLayerListener(eventBuffer);
1377

    
1378
            if (lyr instanceof Classifiable) {
1379
                Classifiable c = (Classifiable) lyr;
1380
                c.removeLegendListener(eventBuffer);
1381
            }
1382

    
1383
            if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore() != null) {
1384
                ((SingleLayer) lyr).getDataStore().deleteObserver(
1385
                        MapContext.this);
1386
            }
1387

    
1388
        }
1389

    
1390
    }
1391

    
1392
    /**
1393
     * <p>
1394
     * Adds the {@link LayerEventListener LayerEventListener} of this map to the
1395
     * collection of layers argument.</p>
1396
     *
1397
     * @param a collection of layers
1398
     */
1399
    public void addAsCollectionListener(FLayers layers2) {
1400
        layers2.addLayerCollectionListener(layerEventListener);
1401
        layers2.addLayerCollectionListener(eventBuffer);
1402
    }
1403

    
1404
    public VectorLayer getGraphicsLayer(String name) {
1405
        return this.tracLayers.get(name);
1406
    }
1407
    
1408
    public Collection<VectorLayer> getGraphicsLayers() {
1409
        return this.tracLayers.values();
1410
    }
1411
    
1412
    public void setGraphicsLayer(String name, FLyrVect layer) {
1413
        this.tracLayers.put(name, layer);
1414
    }
1415
    
1416
    public void removeGraphicsLayer(String name) {
1417
        this.tracLayers.remove(name);
1418
    }
1419
    
1420
    /**
1421
     * <p>
1422
     * Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1423
     *
1424
     * @return the graphic layer of this map
1425
     *
1426
     * @see #setGraphicsLayer(GraphicLayer)
1427
     */
1428
    public GraphicLayer getGraphicsLayer() {
1429
        GraphicLayer tracLayer = (GraphicLayer) this.tracLayers.get(DEFAULT_TRACTLAYER);
1430
        if (tracLayer == null) {
1431
            if (getViewPort() != null) {
1432
                tracLayer
1433
                        = MapContextLocator.getMapContextManager()
1434
                        .createGraphicsLayer(
1435
                                getViewPort().getProjection());
1436
            } else {
1437
                tracLayer
1438
                        = MapContextLocator.getMapContextManager()
1439
                        .createGraphicsLayer(null);
1440
            }
1441
            this.tracLayers.put(DEFAULT_TRACTLAYER, tracLayer);
1442
        }
1443
        return tracLayer;
1444
    }
1445

    
1446
    /**
1447
     * <p>
1448
     * Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1449
     *
1450
     * @param graphicLayer the new graphic layer
1451
     *
1452
     * @see #getGraphicsLayer()
1453
     */
1454
    public void setGraphicsLayer(GraphicLayer graphicLayer) {
1455
        this.tracLayers.put(DEFAULT_TRACTLAYER, graphicLayer);
1456
    }
1457

    
1458
    /**
1459
     * <p>
1460
     * Indicates whether some other object is "equal to" this map.</p>
1461
     * <p>
1462
     * Returns <code>true</code> if success one of this options:
1463
     * <ul>
1464
     * <li>Both objects are equal according to
1465
     * {@linkplain Object#equals(Object)}.
1466
     * <li>Both maps have the same layers.
1467
     * <li>Both maps have the same number of layers and with the same name.
1468
     * </ul>
1469
     * </p>
1470
     *
1471
     * @param obj the reference object with which to compare.
1472
     * @return <code>true</code> if this object is the same as the
1473
     * <code>arg0</code> argument; otherwise <code>false</code>.
1474
     *
1475
     * @see Object#equals(Object)
1476
     */
1477
    public boolean equals(Object arg0) {
1478
        if (!(arg0 instanceof MapContext)) {
1479
            return false;
1480
        }
1481
        MapContext map = (MapContext) arg0;
1482
        if (super.equals(arg0)) {
1483
            return true;
1484
        }
1485
        if (getLayers() == map.getLayers()) {
1486
            return true;
1487
        }
1488
        boolean isEqual = true;
1489
        if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1490
            for (int i = 0; i < getLayers().getLayersCount(); i++) {
1491

    
1492
                if (!getLayers().getLayer(i).getName().equals(
1493
                        map.getLayers().getLayer(i).getName())) {
1494
                    isEqual = false;
1495
                }
1496

    
1497
            }
1498
        } else {
1499
            isEqual = false;
1500
        }
1501
        return isEqual;
1502
    }
1503

    
1504
    /**
1505
     * <p>
1506
     * Registers the message of an error associated to this map.</p>
1507
     *
1508
     * @param stringProperty the error message
1509
     *
1510
     * @see #getLayersError()
1511
     * @see #clearErrors()
1512
     */
1513
    public void addLayerError(String stringProperty) {
1514
        layersError.add(stringProperty);
1515
    }
1516

    
1517
    /**
1518
     * <p>
1519
     * Gets the list with all error messages registered to this map.</p>
1520
     *
1521
     * @return the list of errors registered to this map
1522
     *
1523
     * @see #addLayerError(String)
1524
     * @see #clearErrors()
1525
     */
1526
    public ArrayList getLayersError() {
1527
        return layersError;
1528
    }
1529

    
1530
    /**
1531
     * <p>
1532
     * Removes all error messages associated to this map.</p>
1533
     *
1534
     * @see #addLayerError(String)
1535
     * @see #getLayersError()
1536
     */
1537
    public void clearErrors() {
1538
        layersError.clear();
1539
    }
1540

    
1541
    /**
1542
     * <p>
1543
     * Creates and returns a new group of layers that belongs to this
1544
     * <code>MapContext</code>.</p>
1545
     *
1546
     * @param parent layer node in this <code>MapContexte</code> that will be
1547
     * the parent of the new node
1548
     * @return the new layer node
1549
     */
1550
    public FLayers getNewGroupLayer(FLayers parent) {
1551
        FLayers group1 = new FLayers();//(this,parent);
1552
        group1.setMapContext(this);
1553
        group1.setParentLayer(parent);
1554
        return group1;
1555
    }
1556

    
1557
    public String getClassName() {
1558
        return null;
1559
    }
1560

    
1561
    public Set<FLyrVect> getLayersToSnap() {
1562
        return layersToSnap;
1563
    }
1564

    
1565
    public void setLayersToSnap(Set<FLyrVect> layersToSnap) {
1566
        this.layersToSnap = layersToSnap;
1567

    
1568
    }
1569

    
1570
    public void update(Observable observable, Object notification) {
1571
        // TODO REVISAR ESTO!!!
1572
        String ntype = null;
1573
        if (notification instanceof FeatureStoreNotification) {
1574
            FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1575
            ntype = fsNotification.getType();
1576
            if (ntype.equals(FeatureStoreNotification.LOAD_FINISHED)
1577
                    || ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)) {
1578
                getLayers().moveTo(0, 0);
1579
            }
1580
        }
1581
    }
1582

    
1583
    public long getDrawVersion() {
1584
        if (getViewPort().getDrawVersion() > this.viewPortVersion
1585
                || getLayers().getDrawVersion() > this.layersVersion
1586
                || getGraphicsLayer().getDrawVersion() > graphicsLayerVersion) {
1587
            updateDrawVersion();
1588
        }
1589
        return this.drawVersion;
1590
    }
1591

    
1592
    protected void updateDrawVersion() {
1593
        this.layersVersion = getLayers().getDrawVersion();
1594
        this.viewPortVersion = getViewPort().getDrawVersion();
1595
        this.graphicsLayerVersion = getGraphicsLayer().getDrawVersion();
1596
        this.drawVersion++;
1597
    }
1598

    
1599
    public MapContextDrawer getMapContextDrawer() throws ReadException,
1600
            MapContextException {
1601
        if (this.mapContextDrawer == null) {
1602
            if (mapContextDrawerClass == null) {
1603
                this.mapContextDrawer = MAPCONTEXT_MANAGER
1604
                        .createDefaultMapContextDrawerInstance();
1605
            } else {
1606
                this.mapContextDrawer = MAPCONTEXT_MANAGER
1607
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1608
            }
1609
            this.mapContextDrawer.setMapContext(this);
1610
            this.mapContextDrawer.setViewPort(viewPort);
1611
        }
1612

    
1613
        return this.mapContextDrawer;
1614
    }
1615

    
1616
    public void setMapContextDrawerClass(Class mapContextDrawerClass)
1617
            throws MapContextException {
1618
        MAPCONTEXT_MANAGER.validateMapContextDrawer(mapContextDrawerClass);
1619
        this.mapContextDrawerClass = mapContextDrawerClass;
1620
        if (this.mapContextDrawer != null) {
1621
            this.mapContextDrawer.dispose();
1622
            this.mapContextDrawer = null;
1623
        }
1624
    }
1625

    
1626
    public void setMapContextDrawer(MapContextDrawer drawer) {
1627
        if (this.mapContextDrawer != null) {
1628
            this.mapContextDrawer.dispose();
1629
            this.mapContextDrawer = null;
1630
        }
1631
        this.mapContextDrawer = drawer;
1632
        if (this.mapContextDrawer != null) {
1633
            this.mapContextDrawer.setMapContext(this);
1634
            this.mapContextDrawer.setViewPort(viewPort);
1635
        }
1636
    }
1637

    
1638
    public void loadFromState(PersistentState state)
1639
            throws PersistenceException {
1640

    
1641
        ViewPort vp = (ViewPort) state.get("ViewPort");
1642
        setViewPort(vp);
1643

    
1644
        layers = (FLayers) state.get("layers");
1645
        layers.setName("root layer");
1646
        loadLayers(layers);
1647
        layers.setMapContext(this);
1648

    
1649
        layerEventListener = new LayerEventListener();
1650
        layers.addLayerCollectionListener(layerEventListener);
1651

    
1652
        layers.addLayerCollectionListener(eventBuffer);
1653
        layers.setProjection(vp.getProjection());
1654

    
1655
        //Add the listener for the selection
1656
        addSelectionListener(layers);
1657

    
1658
        // ======================
1659
        if (state.hasValue("orderManager")) {
1660
            LayerOrderManager lom = (LayerOrderManager) state.get("orderManager");
1661
            this.setOrderManager(lom);
1662
        }
1663
        DefaultMapContextManager manager = (DefaultMapContextManager) MapContextLocator.getMapContextManager();
1664
        manager.notifyLoadMapContext(this);
1665
    }
1666

    
1667
    private void loadLayers(FLayers lyrs) {
1668

    
1669
        int sz = lyrs.getLayersCount();
1670
        for (int i = 0; i < sz; i++) {
1671
            try {
1672
                lyrs.getLayer(i).load();
1673
            } catch (LoadLayerException e) {
1674
                LOGGER.error("While loading layer: " + lyrs.getLayer(i).getName());
1675
            }
1676
        }
1677
    }
1678

    
1679
    public void saveToState(PersistentState state) throws PersistenceException {
1680
        state.set("ViewPort", viewPort);
1681
        state.set("layers", (Persistent)layers);
1682
        state.set("orderManager", this.getOrderManager());
1683
    }
1684

    
1685
    public static class RegisterPersistence implements Callable {
1686

    
1687
        public Object call() {
1688
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1689
            DynStruct definition = manager.addDefinition(
1690
                    MapContext.class,
1691
                    "MapContext",
1692
                    "MapContext Persistence definition",
1693
                    null,
1694
                    null
1695
            );
1696
            definition.addDynFieldObject("ViewPort")
1697
                    .setClassOfValue(ViewPort.class)
1698
                    .setMandatory(true);
1699

    
1700
            definition.addDynFieldObject("layers")
1701
                    .setClassOfValue(FLayers.class)
1702
                    .setMandatory(true);
1703

    
1704
            definition.addDynFieldObject("orderManager")
1705
                    .setClassOfValue(LayerOrderManager.class)
1706
                    .setMandatory(false);
1707

    
1708
            return Boolean.TRUE;
1709
        }
1710
    }
1711

    
1712
    protected void doDispose() throws BaseException {
1713
        dispose(layers);
1714
        for (VectorLayer tracLayer : this.tracLayers.values()) {
1715
            dispose(tracLayer);
1716
        }
1717
    }
1718

    
1719
    /**
1720
     * <p>
1721
     * Registers an event buffer as a listener for all layers as argument.</p>
1722
     *
1723
     * <p>
1724
     * Each {@link FLayer FLayer} of this map must have an event buffer for all
1725
     * kind of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1726
     * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers}
1727
     * layers, and for each one, registers, for their specific listeners, the
1728
     * <code>eventBuffer</code> as a listener.</p>
1729
     *
1730
     * @param the layer or layers
1731
     */
1732
    private void addSelectionListener(FLayer lyr) {
1733
        lyr.addLayerListener(eventBuffer);
1734

    
1735
        if (lyr instanceof Classifiable) {
1736
            Classifiable c = (Classifiable) lyr;
1737
            c.addLegendListener(eventBuffer);
1738
        }
1739

    
1740
        if (lyr instanceof FLayers) {
1741
            FLayers lyrs = (FLayers) lyr;
1742
            for (int i = 0; i < lyrs.getLayersCount(); i++) {
1743
                addSelectionListener(lyrs.getLayer(i));
1744
            }
1745
        }
1746
        if (lyr instanceof SingleLayer) {
1747
            if (((SingleLayer) lyr).getDataStore() != null) {
1748
                ((SingleLayer) lyr).getDataStore().addObserver(
1749
                        MapContext.this);
1750
            }
1751
        }
1752
    }
1753

    
1754
    public void setOrderManager(LayerOrderManager lom) {
1755
        orderManager = lom;
1756
    }
1757

    
1758
    public LayerOrderManager getOrderManager() {
1759

    
1760
        if (orderManager == null) {
1761
            orderManager = MapContextLocator.getDefaultOrderManager();
1762
        }
1763
        return orderManager;
1764
    }
1765

    
1766
    public boolean hasVectorLayers() {
1767
        return this.hasVectorLayers(this.getLayers());
1768
    }
1769

    
1770
    public boolean hasActiveVectorLayers() {
1771
        FLayer[] layers = this.getLayers().getActives();
1772
        for (int i = 0; i < layers.length; i++) {
1773
            FLayer layer = layers[i];
1774
            if (layer.isAvailable() && layer instanceof FLyrVect) {
1775
                return true;
1776
            }
1777
        }
1778
        return false;
1779
    }
1780

    
1781
    public boolean hasActiveLayers() {
1782
        FLayer[] layers = this.getLayers().getActives();
1783
        return !ArrayUtils.isEmpty(layers);
1784
    }
1785

    
1786
    public boolean hasLayers() {
1787
        return !this.getLayers().isEmpty();
1788
    }
1789
    
1790
    private boolean hasVectorLayers(FLayers layers) {
1791
        for (int i = 0; i < layers.getLayersCount(); i++) {
1792
            FLayer lyr = layers.getLayer(i);
1793
            if (lyr instanceof FLayers) {
1794
                if (hasVectorLayers((FLayers) lyr)) {
1795
                    return true;
1796
                }
1797
            } else if (lyr instanceof FLyrVect) {
1798
                return true;
1799
            }
1800
        }
1801
        return false;
1802
    }
1803
    
1804
    @Override
1805
    public Iterator<FLayer> iterator() {
1806
        return this.layers.iterator();
1807
    }
1808

    
1809
    public Iterator deepiterator() {
1810
        return this.layers.deepiterator();
1811
    }
1812

    
1813
    public MapTimeContext getTimeContext() {
1814
        TimeSupportManager timeSupportManager = TimeSupportLocator.getManager();
1815
        Interval interval = null;
1816
        final List<Instant> times = new ArrayList<>();
1817
        Instant minInstant = null;
1818
        Instant maxInstant = null;
1819

    
1820
        //TODO Separate absolute and relative time
1821
        FLayers layers = this.getLayers();
1822
        for( Iterator<FLayer> iterator = layers.deepiterator(); iterator.hasNext(); ) {
1823
            FLayer layer = iterator.next();
1824
            if( layer instanceof SingleLayer ) {
1825
                DataStore dataStore = ((SingleLayer) layer).getDataStore();
1826
                if( dataStore.getInterval() != null ) {
1827
                    Instant startInstant = dataStore.getInterval().getStart();
1828
                    Instant endInstant = dataStore.getInterval().getEnd();
1829
                    times.addAll(dataStore.getTimes());
1830
                    if( minInstant == null ) {
1831
                        minInstant = startInstant;
1832
                        maxInstant = endInstant;
1833
                    } else {
1834
                        if( minInstant.isAfter(startInstant) ) {
1835
                            minInstant = startInstant;
1836
                        }
1837
                        if( maxInstant.isBefore(endInstant) ) {
1838
                            maxInstant = endInstant;
1839
                        }
1840
                    }
1841
                }
1842
            }
1843
        }
1844

    
1845
        if( minInstant != null ) {
1846
            if( minInstant.isAbsolute() ) {
1847
                try {
1848
                    interval = timeSupportManager.createAbsoluteInterval((AbsoluteInstant) minInstant, (AbsoluteInstant) maxInstant);
1849
                } catch (AbsoluteIntervalTypeNotRegisteredException e) {
1850
                    LOGGER.warn("Error creating the time interval", e);
1851
                }
1852
            } else {
1853
                interval = timeSupportManager.createRelativeInterval(((RelativeInstant) minInstant).toMillis(), ((RelativeInstant) maxInstant).toMillis());
1854
            }
1855
        }
1856
        final Interval tmp_interval = interval;
1857
        return new MapTimeContext() {
1858
            @Override
1859
            public Interval getInterval() {
1860
                return tmp_interval;
1861
            }
1862

    
1863
            @Override
1864
            public List<Instant> getTimes() {
1865
                return times;
1866
            }
1867
        };
1868
    }
1869

    
1870
    public long getScaleToUseWhenEnvelopeCollapse() {
1871
        return this.scaleToUseWhenEnvelopeCollapse;
1872
    }
1873

    
1874
    public void setScaleToUseWhenEnvelopeCollapse(long scale) {
1875
        this.scaleToUseWhenEnvelopeCollapse = scale;
1876
    }
1877

    
1878
}