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

History | View | Annotate | Download (62.4 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.Iterator;
33
import java.util.LinkedHashMap;
34
import java.util.List;
35
import java.util.Map;
36
import org.apache.commons.lang3.ArrayUtils;
37

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

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

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

    
120
    public interface MapTimeContext {
121
        public Interval getInterval();
122
        public List<Instant> getTimes();
123
    }    
124

    
125
    /**
126
     * @deprecated use getDistanceTrans2Meter()
127
     */
128
    public static final double[] CHANGEM = {1000, 1, 0.01, 0.001, 1609.344,
129
        0.9144, 0.3048, 0.0254, 1 / 8.983152841195214E-6};
130

    
131
    public static ArrayList AREANAMES = new ArrayList();
132
    public static ArrayList AREAABBR = new ArrayList();
133
    public static ArrayList AREATRANS2METER = new ArrayList();
134

    
135
    public static ArrayList DISTANCENAMES = new ArrayList();
136
    public static ArrayList DISTANCEABBR = new ArrayList();
137
    public static ArrayList DISTANCETRANS2METER = new ArrayList();
138

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

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

    
160
    }
161

    
162
    private static final GeometryManager GEOM_MANAGER = GeometryLocator.getGeometryManager();
163

    
164
    private static final MapContextManager MAPCONTEXT_MANAGER = MapContextLocator.getMapContextManager();
165

    
166
    private static final Logger LOGGER = LoggerFactory.getLogger(MapContext.class);
167

    
168
    /**
169
     * <p>
170
     * Determines the number of frames.</p>
171
     *
172
     * <p>
173
     * Number of updates per second that the timer will invoke repaint this
174
     * component.</p>
175
     */
176
    private static int drawFrameRate = 3;
177

    
178
    /**
179
     * <p>
180
     * Returns the draw frame rate.</p>
181
     *
182
     * <p>
183
     * Draw frame rate is the number of repaints of this <code>MapControl</code>
184
     * instance that timer invokes per second.</p>
185
     *
186
     * @return number of repaints of this <code>MapControl</code> instance that
187
     * timer invokes per second
188
     *
189
     * @see #applyFrameRate()
190
     * @see #setDrawFrameRate(int)
191
     */
192
    public static int getDrawFrameRate() {
193
        return drawFrameRate;
194
    }
195

    
196
    /**
197
     * <p>
198
     * Sets the draw frame rate.</p>
199
     *
200
     * <p>
201
     * Draw frame rate is the number of repaints of this <code>MapControl</code>
202
     * instance that timer invokes per second.</p>
203
     *
204
     * @param theDrawFrameRate number of repaints of this <code>MapControl</code>
205
     * instance that timer invokes per second
206
     *
207
     * @see #applyFrameRate()
208
     * @see #getDrawFrameRate()
209
     */
210
    public static void setDrawFrameRate(int theDrawFrameRate) {
211
        drawFrameRate = theDrawFrameRate;
212
    }
213

    
214
    public static void addAreaUnit(String name, String abbr, boolean isLinear, double trans2meter) {
215
        if (!AREANAMES.contains(name)) {
216
            AREANAMES.add(name);
217
            String pow = "";
218
            if (isLinear) {
219
                pow = String.valueOf((char) 178);
220
            }
221
            AREAABBR.add(abbr + pow);
222
            AREATRANS2METER.add(trans2meter);
223
        }
224
    }
225

    
226
    public static String[] getAreaNames() {
227
        return (String[]) AREANAMES.toArray(new String[0]);
228
    }
229

    
230
    public static String[] getAreaAbbr() {
231
        return (String[]) AREAABBR.toArray(new String[0]);
232
    }
233

    
234
    public static double[] getAreaTrans2Meter() {
235
        int size = AREATRANS2METER.size();
236
        double[] trans2meters = new double[size];
237
        for (int i = 0; i < size; i++) {
238
            trans2meters[i] = ((Double) AREATRANS2METER.get(i));
239
        }
240
        return trans2meters;
241
    }
242

    
243
    public static String getOfLinear(int i) {
244
        if (((String) AREAABBR.get(i)).toLowerCase().endsWith(String.valueOf((char) 178))) {
245
            return String.valueOf((char) 178);
246
        }
247
        return "";
248
    }
249

    
250
    public static void addDistanceUnit(String name, String abbr, double trans2meter) {
251
        if (!DISTANCENAMES.contains(name)) {
252
            DISTANCENAMES.add(name);
253
            DISTANCEABBR.add(abbr);
254
            DISTANCETRANS2METER.add(trans2meter);
255
        }
256
    }
257

    
258
    public static String[] getDistanceNames() {
259
        return (String[]) DISTANCENAMES.toArray(new String[0]);
260
    }
261

    
262
    public String getDistanceName() {
263
        return (String) DISTANCENAMES.get(this.getViewPort().getDistanceUnits());
264
    }
265

    
266
    public static String[] getDistanceAbbr() {
267
        return (String[]) DISTANCEABBR.toArray(new String[0]);
268
    }
269

    
270
    public static double[] getDistanceTrans2Meter() {
271
        int size = DISTANCETRANS2METER.size();
272
        double[] trans2meters = new double[size];
273
        for (int i = 0; i < size; i++) {
274
            trans2meters[i] = ((Double) DISTANCETRANS2METER.get(i));
275
        }
276
        return trans2meters;
277
    }
278

    
279
    public static int getDistancePosition(String s) {
280
        for (int i = 0; i < DISTANCENAMES.size(); i++) {
281
            if (DISTANCENAMES.get(i).equals(s)) {
282
                return i;
283
            }
284
        }
285
        return 0;
286
    }
287

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

    
340
    public static final int EQUALS = 0;
341
    public static final int DISJOINT = 1;
342
    public static final int INTERSECTS = 2;
343
    public static final int TOUCHES = 3;
344
    public static final int CROSSES = 4;
345
    public static final int WITHIN = 5;
346
    public static final int CONTAINS = 6;
347
    public static final int OVERLAPS = 7;
348

    
349
    /**
350
     * A hierarchy of {@link FLayers FLayers} nodes.
351
     */
352
    protected FLayers layers;
353

    
354
    /**
355
     * A List of layers with graphical items: geometries and symbols.
356
     */
357
    private static final String DEFAULT_TRACTLAYER = "Default";
358
    private Map<String, VectorLayer> tracLayers;
359

    
360
    /**
361
     * Information for draw layers in a view.
362
     *
363
     * @see #getViewPort()
364
     * @see #setViewPort(ViewPort)
365
     */
366
    private ViewPort viewPort;
367
    // private ArrayList invalidationListeners = new ArrayList();
368
    /**
369
     * Array list with all {@link LegendListener LegendListener} registered to
370
     * this map.
371
     *
372
     * @see #addLayerListener(LegendListener)
373
     * @see #removeLayerListener(LegendListener)
374
     * @see #callLegendChanged()
375
     */
376
    private List legendListeners;
377

    
378
    /**
379
     * Array list with all {@link LayerDrawingListener LayerDrawingListener}
380
     * registered to this map.
381
     *
382
     * @see #addLayerDrawingListener(LayerDrawingListener)
383
     * @see #removeLayerDrawListener(LayerDrawingListener)
384
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
385
     */
386
    private ArrayList layerDrawingListeners = new ArrayList();
387

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

    
409
    /**
410
     * Event listener for the collection of layers of this map.
411
     */
412
    private LayerEventListener layerEventListener = null;
413

    
414
    /**
415
     * List with information of all errors produced on all layers.
416
     *
417
     * @see #addLayerError(String)
418
     * @see #getLayersError()
419
     * @see #clearErrors()
420
     */
421
    private ArrayList layersError = new ArrayList();
422

    
423
    /**
424
     * Array list with all {@link ErrorListener ErrorListener} registered to
425
     * this map.
426
     *
427
     * @see #addErrorListener(ErrorListener)
428
     * @see #removeErrorListener(LegendListener)
429
     * @see #reportDriverExceptions(String, List)
430
     */
431
    private ArrayList errorListeners = new ArrayList();
432

    
433
    /**
434
     * Layer order manager decides position of layers added to this map context.
435
     */
436
    private LayerOrderManager orderManager = null;
437

    
438
        // public static ResourceBundle myResourceBundle =
439
    // ResourceBundle.getBundle("FMap");
440
    /**
441
     * <p>
442
     * Default <i>zoom in</i> factor.</p>
443
     * <p>
444
     * Doing a <i>zoom in</i> operation, decreases the focal distance and
445
     * increases the eyesight angle to the surface. This allows view an smaller
446
     * area but with the items bigger.</p>
447
     */
448
    public static double ZOOMINFACTOR = 2;
449

    
450
    /**
451
     * <p>
452
     * Default <i>zoom out</i> factor.</p>
453
     * <p>
454
     * Doing a <i>zoom out</i> operation, increases the focal distance and
455
     * decreases the eyesight angle to the surface. This allows view a bigger
456
     * area but with the items smaller.</p>
457
     */
458
    public static double ZOOMOUTFACTOR = 0.5;
459

    
460
    /**
461
     *          * Draw version of the context. It's used for know when de componend has
462
     * changed any visualization property
463
     *
464
     * @see getDrawVersion
465
     * @see updateDrawVersion
466
     */
467
    private long drawVersion = 0L;
468

    
469
    private long layersVersion = 0L;
470
    private long viewPortVersion = 0L;
471
    private long graphicsLayerVersion = 0L;
472

    
473
    /**
474
     * Object to Manage Draw of MapContext
475
     */
476
    private MapContextDrawer mapContextDrawer = null;
477

    
478
    /**
479
     * Object to Manage Draw of MapContext
480
     */
481
    private Class mapContextDrawerClass = null;
482

    
483
    /**
484
     * <p>
485
     * Color used to represent the selections.</p>
486
     */
487
    private static Color selectionColor = Color.YELLOW;
488
    private ArrayList layersToSnap = new ArrayList();
489

    
490
    /**
491
     * <p>
492
     * Gets the color used to represent the selections.</p>
493
     *
494
     * @return color used to represent the selections
495
     */
496
    public static Color getSelectionColor() {
497
        return selectionColor;
498
    }
499

    
500
    /**
501
     * <p>
502
     * Sets the color used to represent the selections.</p>
503
     *
504
     * @param selectionColor color used to represent the selections
505
     */
506
    public static void setSelectionColor(Color selectionColor) {
507
        MapContext.selectionColor = selectionColor;
508
    }
509

    
510
    /**
511
     * <p>
512
     * Creates a new map context with the drawing information defined in the
513
     * view port argument, and without layers.</p>
514
     *
515
     * @param vp information for drawing the layers of this map in the available
516
     * rectangular area according a projection
517
     */
518
    public MapContext(ViewPort vp) {
519
        this(new FLayers(), vp);
520
    }
521

    
522
    public MapContext() {
523
        this.legendListeners = new ArrayList();
524
        this.tracLayers = new LinkedHashMap<>();
525
        this.layerEventListener = new LayerEventListener();
526
    }
527

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

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

    
570
    /**
571
     * <p>
572
     * Adds the specified legend listener (if didn't exist) to receive legend
573
     * events from this map.</p>
574
     *
575
     * @param listener the legend listener
576
     *
577
     * @see #removeLayerListener(LegendListener)
578
     * @see #callLegendChanged()
579
     */
580
    public void addLayerListener(LegendListener listener) {
581
        if (!legendListeners.contains(listener)) {
582
            legendListeners.add(listener);
583
        }
584
    }
585

    
586
    /**
587
     * <p>
588
     * Adds the specified layer drawing listener to catch and handle drawing
589
     * events from layers of this map.</p>
590
     *
591
     * @param listener the listener to add
592
     *
593
     * @see #removeLayerDrawListener(LayerDrawingListener)
594
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
595
     */
596
    public void addLayerDrawingListener(LayerDrawingListener listener) {
597
        layerDrawingListeners.add(listener);
598
    }
599

    
600
    /**
601
     * <p>
602
     * Removes the specified layer drawing listener from this map.</p>
603
     *
604
     * @param listener the listener to remove
605
     *
606
     * @see #addLayerDrawingListener(LayerDrawingListener)
607
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
608
     */
609
    public void removeLayerDrawListener(LayerDrawingListener listener) {
610
        layerDrawingListeners.remove(listener);
611
    }
612

    
613
    /**
614
     * <p>
615
     * Adds the specified error listener to receive error events from this
616
     * map.</p>
617
     *
618
     * @param listener the listener to add
619
     *
620
     * @see #removeErrorListener(LegendListener)
621
     * @see #reportDriverExceptions(String, List)
622
     */
623
    public void addErrorListener(ErrorListener listener) {
624
        errorListeners.add(listener);
625
    }
626

    
627
    /**
628
     * <p>
629
     * Removes the specified error listener from this map.</p>
630
     *
631
     * @param listener the listener to remove
632
     *
633
     * @see #addErrorListener(ErrorListener)
634
     * @see #reportDriverExceptions(String, List)
635
     */
636
    public void removeErrorListener(LegendListener listener) {
637
        legendListeners.remove(listener);
638
    }
639

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

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

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

    
708
    /**
709
     * <p>
710
     * Removes the specified layer listener from this map.</p>
711
     *
712
     * @param listener the listener to remove
713
     *
714
     * @see #addLayerListener(LegendListener)
715
     * @see #callLegendChanged()
716
     */
717
    public void removeLayerListener(LegendListener listener) {
718
        legendListeners.remove(listener);
719
    }
720

    
721
    /**
722
     * <p>
723
     * Returns the hierarchy of {@link FLayers FLayers} nodes stored in this
724
     * map.</p>
725
     *
726
     * @return the hierarchy of nodes of layers stored in this map
727
     */
728
    public FLayers getLayers() {
729
        return layers;
730
    }
731

    
732
    /**
733
     * <p>
734
     * Draws the visible layers of this map according its view port, on the
735
     * image parameter.</p>
736
     *
737
     * @param b image with an accessible buffer of image data
738
     */
739
    public void drawLabels(BufferedImage b) {
740
    }
741

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

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

    
783
        CompatLocator.getGraphicsUtils().setRenderingHintsForPrinting(g);
784

    
785
        Cancellable cancel = new Cancellable() {
786
            public boolean isCanceled() {
787
                return false;
788
            }
789

    
790
            public void setCanceled(boolean canceled) {
791
                // No queremos que se pueda cancelar la impresi?n.
792

    
793
            }
794
        };
795
        this.getMapContextDrawer().print(this.layers, g, cancel, scale, properties);
796
        for (VectorLayer tracLayer : this.tracLayers.values()) {
797
            if (tracLayer != null) {
798
                tracLayer.draw(null, g, viewPort, cancel, scale);
799
            }
800
        }
801
    }
802

    
803
    /**
804
     * <p>
805
     * Returns a new <code>MapContext</code> instance with the information of
806
     * the <code>vp</code> argument, and the layers of this map.</p>
807
     *
808
     * @param vp information for drawing the layers of this map in the available
809
     * rectangular area according a projection
810
     *
811
     * @return a new <code>MapContext</code> instance projected by
812
     * <code>vp</code>
813
     */
814
    public MapContext createNewFMap(ViewPort vp) {
815
        MapContext ret = new MapContext(vp);
816
        ret.layers = this.layers;
817

    
818
        return ret;
819
    }
820

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

    
846
        /*
847
         * Cloning order manager
848
         */
849
        LayerOrderManager lom = this.getOrderManager();
850
        try {
851
            lom = (LayerOrderManager) lom.clone();
852
            ret.setOrderManager(lom);
853
        } catch (CloneNotSupportedException e1) {
854
            LOGGER.error("While cloning order manager", e1);
855
        }
856

    
857
        FLayers aux = new FLayers();//(ret,null);
858
        aux.setMapContext(ret);
859
        for (int i = 0; i < antLayers.getLayersCount(); i++) {
860
            FLayer lyr = antLayers.getLayer(i);
861
            try {
862
                FLayer auxLayer = lyr.cloneLayer();
863
                aux.addLayer(auxLayer);
864
                auxLayer.dispose();
865
            } catch (Exception e) {
866
                throw new RuntimeException(e);
867
            }
868
        }
869
        ret.layers = aux;
870
        return ret;
871

    
872
    }
873

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

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

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

    
920
        if (viewPort.getImageSize() == null) {
921
            return -1;
922
        }
923

    
924
        if (viewPort.getAdjustedEnvelope() == null) {
925
            return 0;
926
        }
927
        double[] trans2Meter = getDistanceTrans2Meter();
928
        int mUnits = getViewPort().getMapUnits();
929

    
930
        if (proj == null) {
931
            double w = ((viewPort.getImageSize().width / dpi) * 0.0254);
932
            return (long) (viewPort.getAdjustedEnvelope().getLength(0)
933
                    / w * trans2Meter[mUnits]);
934
        }
935

    
936
        return Math.round(proj.getScale(
937
                viewPort.getAdjustedEnvelope().getMinimum(0) * trans2Meter[mUnits],
938
                viewPort.getAdjustedEnvelope().getMaximum(0) * trans2Meter[mUnits],
939
                viewPort.getImageSize().width,
940
                dpi));
941

    
942
    }
943

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

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

    
990
    /**
991
     * @see
992
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
993
     */
994
    public void process(Visitor visitor) {
995
    }
996

    
997
    /**
998
     * @see
999
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
1000
     */
1001
    public void processSelected(Visitor visitor) {
1002
    }
1003

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

    
1012
    /**
1013
     * @see
1014
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
1015
     */
1016
    public void selectFromSelection() {
1017
    }
1018

    
1019
    /**
1020
     * @see
1021
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
1022
     */
1023
    public void createIndex() {
1024
    }
1025

    
1026
    /**
1027
     * @see org.cresques.geo.Projected#getProjection()
1028
     *
1029
     * @see ViewPort#getProjection()
1030
     * @see #setProjection(IProjection)
1031
     * @see #reProject(ICoordTrans)
1032
     */
1033
    public IProjection getProjection() {
1034
        return getViewPort().getProjection();
1035
    }
1036

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

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

    
1060
    public Envelope getSelectionBounds() throws BaseException {
1061

    
1062
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1063

    
1064
        layers.accept(visitor);
1065
        Envelope env_in_data_crs = visitor.getSelectioEnvelope();
1066
        return env_in_data_crs;
1067
    }
1068

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

    
1104
        CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1105

    
1106
        this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1107
    }
1108

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

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

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

    
1169
            public void setCanceled(boolean canceled) {
1170
                // Do nothing
1171
            }
1172
        }, scale);
1173
    }
1174

    
1175
    /**
1176
     * <p>
1177
     * Gets the {@link ViewPort ViewPort} associated to this map.</p>
1178
     *
1179
     * @return the view port
1180
     *
1181
     * @see #setViewPort(ViewPort)
1182
     */
1183
    public ViewPort getViewPort() {
1184
        return viewPort;
1185
    }
1186

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

    
1204
        if (this.mapContextDrawer != null) {
1205
            this.mapContextDrawer.setViewPort(viewPort);
1206
        }
1207

    
1208
        this.viewPort = viewPort;
1209
        if (viewPort != null) {
1210
            viewPort.addViewPortListener(eventBuffer);
1211
        }
1212
    }
1213

    
1214
    /**
1215
     * <p>
1216
     * Sets the given extent to the {@link ViewPort ViewPort} and updates the
1217
     * view with the new zoom.</p>
1218
     *
1219
     * @param extent the extent of the new zoom
1220
     */
1221
    public void zoomToEnvelope(Envelope extent) {
1222
        if (extent != null && !extent.isEmpty()) {
1223
            getViewPort().setEnvelope(extent);
1224
        }
1225
    }
1226

    
1227
    /**
1228
     * <p>
1229
     * Returns the union of all extents of all layers of this map.</p>
1230
     *
1231
     * @return full extent of layers of this map
1232
     * @throws ReadDriverException if the driver fails reading.
1233
     *
1234
     * @see FLayers#getFullEnvelope()
1235
     */
1236
    public Envelope getFullEnvelope() throws ReadException {
1237
        Envelope envelope = layers.getFullEnvelope();
1238

    
1239
        for (VectorLayer tracLayer : this.tracLayers.values()) {
1240
            if (tracLayer != null) {
1241
                Envelope graphicsEnvelope = tracLayer.getFullEnvelope();
1242
                if( graphicsEnvelope==null ) {
1243
                    continue;
1244
                }
1245
                if (envelope == null) {
1246
                    try {
1247
                        envelope =  (Envelope) graphicsEnvelope.clone();
1248
                    } catch (CloneNotSupportedException ex) {
1249
                    }
1250
                } else if (graphicsEnvelope != null) {
1251
                    envelope.add(graphicsEnvelope);
1252
                }
1253
            }
1254
        }
1255
        return envelope;
1256
    }
1257

    
1258
    /**
1259
     * <p>
1260
     * Adds a listener of atomic events to the internal
1261
     * {@link EventBuffer EventBuffer}.</p>
1262
     *
1263
     * @param listener the new listener
1264
     *
1265
     * @return <code>true</code> if has added the listener successfully
1266
     *
1267
     * @see #removeAtomicEventListener(AtomicEventListener)
1268
     * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1269
     */
1270
    public boolean addAtomicEventListener(AtomicEventListener listener) {
1271
        return eventBuffer.addAtomicEventListener(listener);
1272
    }
1273

    
1274
    /**
1275
     * <p>
1276
     * Removes a listener of atomic events from the internal
1277
     * {@link EventBuffer EventBuffer}.</p>
1278
     *
1279
     * @param listener the listener to remove
1280
     *
1281
     * @return <tt>true</tt> if the list contained the specified element
1282
     *
1283
     * @see #addAtomicEventListener(AtomicEventListener)
1284
     * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1285
     */
1286
    public boolean removeAtomicEventListener(AtomicEventListener listener) {
1287
        return eventBuffer.removeAtomicEventListener(listener);
1288
    }
1289

    
1290
    /**
1291
     * @see EventBuffer#beginAtomicEvent()
1292
     *
1293
     * @see #endAtomicEvent()
1294
     */
1295
    public void beginAtomicEvent() {
1296
        eventBuffer.beginAtomicEvent();
1297
    }
1298

    
1299
    /**
1300
     * @see EventBuffer#endAtomicEvent()
1301
     *
1302
     * @see #beginAtomicEvent()
1303
     */
1304
    public void endAtomicEvent() {
1305
        eventBuffer.endAtomicEvent();
1306
    }
1307

    
1308
    /**
1309
     * <p>
1310
     * The class <code>LayerEventListener</code> implements the methods of
1311
     * {@link LayerCollectionListener LayerCollectionListener} that handles the
1312
     * "layer added" or "layer removed" events in a map.</p>
1313
     * <p>
1314
     * Is designed as a listener for all layers in a
1315
     * {@link MapContext MapContext}.</p>
1316
     *
1317
     * @author Fernando Gonz?lez Cort?s
1318
     */
1319
    public class LayerEventListener extends BaseLayerCollectionListener {
1320

    
1321
        @Override
1322
        public void layerAdded(LayerCollectionEvent e) {
1323
            // Voy a mover todo esto a la vista
1324
//            // Si aun no tenemos envelope, problablemente sera la primera capa,
1325
//            // asi que asignamos el envelope de la capa al mapcontext.
1326
//            if (getViewPort().getEnvelope() == null) {
1327
//                FLayer lyr = e.getAffectedLayer();
1328
//                if (lyr.isAvailable()) {
1329
//                    try {
1330
//                        getViewPort().setEnvelope(lyr.getFullEnvelope());
1331
//                    } catch (Exception ex) {
1332
//                        logger.warn(
1333
//                                MessageFormat.format(
1334
//                                    "Can''t set envelope to view port from layer ''{0}''",
1335
//                                    new Object[]{lyr.getName()}
1336
//                                ),
1337
//                                ex
1338
//                        );
1339
//                    }
1340
//                }
1341
//            }
1342

    
1343
            // Registramos al FMap como listener del legend de las capas
1344
            FLayer lyr = e.getAffectedLayer();
1345
            addSelectionListener(lyr);
1346
        }
1347

    
1348
        @Override
1349
        public void layerRemoved(LayerCollectionEvent e) {
1350
            FLayer lyr = e.getAffectedLayer();
1351

    
1352
            lyr.removeLayerListener(eventBuffer);
1353

    
1354
            if (lyr instanceof Classifiable) {
1355
                Classifiable c = (Classifiable) lyr;
1356
                c.removeLegendListener(eventBuffer);
1357
            }
1358

    
1359
            if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore() != null) {
1360
                ((SingleLayer) lyr).getDataStore().deleteObserver(
1361
                        MapContext.this);
1362
            }
1363

    
1364
        }
1365

    
1366
    }
1367

    
1368
    /**
1369
     * <p>
1370
     * Adds the {@link LayerEventListener LayerEventListener} of this map to the
1371
     * collection of layers argument.</p>
1372
     *
1373
     * @param a collection of layers
1374
     */
1375
    public void addAsCollectionListener(FLayers layers2) {
1376
        layers2.addLayerCollectionListener(layerEventListener);
1377
        layers2.addLayerCollectionListener(eventBuffer);
1378
    }
1379

    
1380
    public VectorLayer getGraphicsLayer(String name) {
1381
        return this.tracLayers.get(name);
1382
    }
1383
    
1384
    public Collection<VectorLayer> getGraphicsLayers() {
1385
        return this.tracLayers.values();
1386
    }
1387
    
1388
    public void setGraphicsLayer(String name, FLyrVect layer) {
1389
        this.tracLayers.put(name, layer);
1390
    }
1391
    
1392
    public void removeGraphicsLayer(String name) {
1393
        this.tracLayers.remove(name);
1394
    }
1395
    
1396
    /**
1397
     * <p>
1398
     * Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1399
     *
1400
     * @return the graphic layer of this map
1401
     *
1402
     * @see #setGraphicsLayer(GraphicLayer)
1403
     */
1404
    public GraphicLayer getGraphicsLayer() {
1405
        GraphicLayer tracLayer = (GraphicLayer) this.tracLayers.get(DEFAULT_TRACTLAYER);
1406
        if (tracLayer == null) {
1407
            if (getViewPort() != null) {
1408
                tracLayer
1409
                        = MapContextLocator.getMapContextManager()
1410
                        .createGraphicsLayer(
1411
                                getViewPort().getProjection());
1412
            } else {
1413
                tracLayer
1414
                        = MapContextLocator.getMapContextManager()
1415
                        .createGraphicsLayer(null);
1416
            }
1417
            this.tracLayers.put(DEFAULT_TRACTLAYER, tracLayer);
1418
        }
1419
        return tracLayer;
1420
    }
1421

    
1422
    /**
1423
     * <p>
1424
     * Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1425
     *
1426
     * @param graphicLayer the new graphic layer
1427
     *
1428
     * @see #getGraphicsLayer()
1429
     */
1430
    public void setGraphicsLayer(GraphicLayer graphicLayer) {
1431
        this.tracLayers.put(DEFAULT_TRACTLAYER, graphicLayer);
1432
    }
1433

    
1434
    /**
1435
     * <p>
1436
     * Indicates whether some other object is "equal to" this map.</p>
1437
     * <p>
1438
     * Returns <code>true</code> if success one of this options:
1439
     * <ul>
1440
     * <li>Both objects are equal according to
1441
     * {@linkplain Object#equals(Object)}.
1442
     * <li>Both maps have the same layers.
1443
     * <li>Both maps have the same number of layers and with the same name.
1444
     * </ul>
1445
     * </p>
1446
     *
1447
     * @param obj the reference object with which to compare.
1448
     * @return <code>true</code> if this object is the same as the
1449
     * <code>arg0</code> argument; otherwise <code>false</code>.
1450
     *
1451
     * @see Object#equals(Object)
1452
     */
1453
    public boolean equals(Object arg0) {
1454
        if (!(arg0 instanceof MapContext)) {
1455
            return false;
1456
        }
1457
        MapContext map = (MapContext) arg0;
1458
        if (super.equals(arg0)) {
1459
            return true;
1460
        }
1461
        if (getLayers() == map.getLayers()) {
1462
            return true;
1463
        }
1464
        boolean isEqual = true;
1465
        if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1466
            for (int i = 0; i < getLayers().getLayersCount(); i++) {
1467

    
1468
                if (!getLayers().getLayer(i).getName().equals(
1469
                        map.getLayers().getLayer(i).getName())) {
1470
                    isEqual = false;
1471
                }
1472

    
1473
            }
1474
        } else {
1475
            isEqual = false;
1476
        }
1477
        return isEqual;
1478
    }
1479

    
1480
    /**
1481
     * <p>
1482
     * Registers the message of an error associated to this map.</p>
1483
     *
1484
     * @param stringProperty the error message
1485
     *
1486
     * @see #getLayersError()
1487
     * @see #clearErrors()
1488
     */
1489
    public void addLayerError(String stringProperty) {
1490
        layersError.add(stringProperty);
1491
    }
1492

    
1493
    /**
1494
     * <p>
1495
     * Gets the list with all error messages registered to this map.</p>
1496
     *
1497
     * @return the list of errors registered to this map
1498
     *
1499
     * @see #addLayerError(String)
1500
     * @see #clearErrors()
1501
     */
1502
    public ArrayList getLayersError() {
1503
        return layersError;
1504
    }
1505

    
1506
    /**
1507
     * <p>
1508
     * Removes all error messages associated to this map.</p>
1509
     *
1510
     * @see #addLayerError(String)
1511
     * @see #getLayersError()
1512
     */
1513
    public void clearErrors() {
1514
        layersError.clear();
1515
    }
1516

    
1517
    /**
1518
     * <p>
1519
     * Creates and returns a new group of layers that belongs to this
1520
     * <code>MapContext</code>.</p>
1521
     *
1522
     * @param parent layer node in this <code>MapContexte</code> that will be
1523
     * the parent of the new node
1524
     * @return the new layer node
1525
     */
1526
    public FLayers getNewGroupLayer(FLayers parent) {
1527
        FLayers group1 = new FLayers();//(this,parent);
1528
        group1.setMapContext(this);
1529
        group1.setParentLayer(parent);
1530
        return group1;
1531
    }
1532

    
1533
    public String getClassName() {
1534
        return null;
1535
    }
1536

    
1537
    public ArrayList getLayersToSnap() {
1538
        return layersToSnap;
1539
    }
1540

    
1541
    public void setLayersToSnap(ArrayList layersToSnap) {
1542
        this.layersToSnap = layersToSnap;
1543

    
1544
    }
1545

    
1546
    public void update(Observable observable, Object notification) {
1547
        // TODO REVISAR ESTO!!!
1548
        String ntype = null;
1549
        if (notification instanceof FeatureStoreNotification) {
1550
            FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1551
            ntype = fsNotification.getType();
1552
            if (ntype.equals(FeatureStoreNotification.LOAD_FINISHED)
1553
                    || ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)) {
1554
                getLayers().moveTo(0, 0);
1555
            }
1556
        }
1557
    }
1558

    
1559
    public long getDrawVersion() {
1560
        if (getViewPort().getDrawVersion() > this.viewPortVersion
1561
                || getLayers().getDrawVersion() > this.layersVersion
1562
                || getGraphicsLayer().getDrawVersion() > graphicsLayerVersion) {
1563
            updateDrawVersion();
1564
        }
1565
        return this.drawVersion;
1566
    }
1567

    
1568
    protected void updateDrawVersion() {
1569
        this.layersVersion = getLayers().getDrawVersion();
1570
        this.viewPortVersion = getViewPort().getDrawVersion();
1571
        this.graphicsLayerVersion = getGraphicsLayer().getDrawVersion();
1572
        this.drawVersion++;
1573
    }
1574

    
1575
    public MapContextDrawer getMapContextDrawer() throws ReadException,
1576
            MapContextException {
1577
        if (this.mapContextDrawer == null) {
1578
            if (mapContextDrawerClass == null) {
1579
                this.mapContextDrawer = MAPCONTEXT_MANAGER
1580
                        .createDefaultMapContextDrawerInstance();
1581
            } else {
1582
                this.mapContextDrawer = MAPCONTEXT_MANAGER
1583
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1584
            }
1585
            this.mapContextDrawer.setMapContext(this);
1586
            this.mapContextDrawer.setViewPort(viewPort);
1587
        }
1588

    
1589
        return this.mapContextDrawer;
1590
    }
1591

    
1592
    public void setMapContextDrawerClass(Class mapContextDrawerClass)
1593
            throws MapContextException {
1594
        MAPCONTEXT_MANAGER.validateMapContextDrawer(mapContextDrawerClass);
1595
        this.mapContextDrawerClass = mapContextDrawerClass;
1596
        if (this.mapContextDrawer != null) {
1597
            this.mapContextDrawer.dispose();
1598
            this.mapContextDrawer = null;
1599
        }
1600
    }
1601

    
1602
    public void setMapContextDrawer(MapContextDrawer drawer) {
1603
        if (this.mapContextDrawer != null) {
1604
            this.mapContextDrawer.dispose();
1605
            this.mapContextDrawer = null;
1606
        }
1607
        this.mapContextDrawer = drawer;
1608
        if (this.mapContextDrawer != null) {
1609
            this.mapContextDrawer.setMapContext(this);
1610
            this.mapContextDrawer.setViewPort(viewPort);
1611
        }
1612
    }
1613

    
1614
    public void loadFromState(PersistentState state)
1615
            throws PersistenceException {
1616

    
1617
        ViewPort vp = (ViewPort) state.get("ViewPort");
1618
        setViewPort(vp);
1619

    
1620
        layers = (FLayers) state.get("layers");
1621
        layers.setName("root layer");
1622
        loadLayers(layers);
1623
        layers.setMapContext(this);
1624

    
1625
        layerEventListener = new LayerEventListener();
1626
        layers.addLayerCollectionListener(layerEventListener);
1627

    
1628
        layers.addLayerCollectionListener(eventBuffer);
1629
        layers.setProjection(vp.getProjection());
1630

    
1631
        //Add the listener for the selection
1632
        addSelectionListener(layers);
1633

    
1634
        // ======================
1635
        if (state.hasValue("orderManager")) {
1636
            LayerOrderManager lom = (LayerOrderManager) state.get("orderManager");
1637
            this.setOrderManager(lom);
1638
        }
1639
        DefaultMapContextManager manager = (DefaultMapContextManager) MapContextLocator.getMapContextManager();
1640
        manager.notifyLoadMapContext(this);
1641
    }
1642

    
1643
    private void loadLayers(FLayers lyrs) {
1644

    
1645
        int sz = lyrs.getLayersCount();
1646
        for (int i = 0; i < sz; i++) {
1647
            try {
1648
                lyrs.getLayer(i).load();
1649
            } catch (LoadLayerException e) {
1650
                LOGGER.error("While loading layer: " + lyrs.getLayer(i).getName());
1651
            }
1652
        }
1653
    }
1654

    
1655
    public void saveToState(PersistentState state) throws PersistenceException {
1656
        state.set("ViewPort", viewPort);
1657
        state.set("layers", (Persistent)layers);
1658
        state.set("orderManager", this.getOrderManager());
1659
    }
1660

    
1661
    public static class RegisterPersistence implements Callable {
1662

    
1663
        public Object call() {
1664
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1665
            DynStruct definition = manager.addDefinition(
1666
                    MapContext.class,
1667
                    "MapContext",
1668
                    "MapContext Persistence definition",
1669
                    null,
1670
                    null
1671
            );
1672
            definition.addDynFieldObject("ViewPort")
1673
                    .setClassOfValue(ViewPort.class)
1674
                    .setMandatory(true);
1675

    
1676
            definition.addDynFieldObject("layers")
1677
                    .setClassOfValue(FLayers.class)
1678
                    .setMandatory(true);
1679

    
1680
            definition.addDynFieldObject("orderManager")
1681
                    .setClassOfValue(LayerOrderManager.class)
1682
                    .setMandatory(false);
1683

    
1684
            return Boolean.TRUE;
1685
        }
1686
    }
1687

    
1688
    protected void doDispose() throws BaseException {
1689
        dispose(layers);
1690
        for (VectorLayer tracLayer : this.tracLayers.values()) {
1691
            dispose(tracLayer);
1692
        }
1693
    }
1694

    
1695
    /**
1696
     * <p>
1697
     * Registers an event buffer as a listener for all layers as argument.</p>
1698
     *
1699
     * <p>
1700
     * Each {@link FLayer FLayer} of this map must have an event buffer for all
1701
     * kind of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1702
     * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers}
1703
     * layers, and for each one, registers, for their specific listeners, the
1704
     * <code>eventBuffer</code> as a listener.</p>
1705
     *
1706
     * @param the layer or layers
1707
     */
1708
    private void addSelectionListener(FLayer lyr) {
1709
        lyr.addLayerListener(eventBuffer);
1710

    
1711
        if (lyr instanceof Classifiable) {
1712
            Classifiable c = (Classifiable) lyr;
1713
            c.addLegendListener(eventBuffer);
1714
        }
1715

    
1716
        if (lyr instanceof FLayers) {
1717
            FLayers lyrs = (FLayers) lyr;
1718
            for (int i = 0; i < lyrs.getLayersCount(); i++) {
1719
                addSelectionListener(lyrs.getLayer(i));
1720
            }
1721
        }
1722
        if (lyr instanceof SingleLayer) {
1723
            if (((SingleLayer) lyr).getDataStore() != null) {
1724
                ((SingleLayer) lyr).getDataStore().addObserver(
1725
                        MapContext.this);
1726
            }
1727
        }
1728
    }
1729

    
1730
    public void setOrderManager(LayerOrderManager lom) {
1731
        orderManager = lom;
1732
    }
1733

    
1734
    public LayerOrderManager getOrderManager() {
1735

    
1736
        if (orderManager == null) {
1737
            orderManager = MapContextLocator.getDefaultOrderManager();
1738
        }
1739
        return orderManager;
1740
    }
1741

    
1742
    public boolean hasVectorLayers() {
1743
        return this.hasVectorLayers(this.getLayers());
1744
    }
1745

    
1746
    public boolean hasActiveVectorLayers() {
1747
        FLayer[] layers = this.getLayers().getActives();
1748
        for (int i = 0; i < layers.length; i++) {
1749
            FLayer layer = layers[i];
1750
            if (layer.isAvailable() && layer instanceof FLyrVect) {
1751
                return true;
1752
            }
1753
        }
1754
        return false;
1755
    }
1756

    
1757
    public boolean hasActiveLayers() {
1758
        FLayer[] layers = this.getLayers().getActives();
1759
        return !ArrayUtils.isEmpty(layers);
1760
    }
1761

    
1762
    public boolean hasLayers() {
1763
        return !this.getLayers().isEmpty();
1764
    }
1765
    
1766
    private boolean hasVectorLayers(FLayers layers) {
1767
        for (int i = 0; i < layers.getLayersCount(); i++) {
1768
            FLayer lyr = layers.getLayer(i);
1769
            if (lyr instanceof FLayers) {
1770
                if (hasVectorLayers((FLayers) lyr)) {
1771
                    return true;
1772
                }
1773
            } else if (lyr instanceof FLyrVect) {
1774
                return true;
1775
            }
1776
        }
1777
        return false;
1778
    }
1779
    
1780
    @Override
1781
    public Iterator<FLayer> iterator() {
1782
        return this.layers.iterator();
1783
    }
1784

    
1785
    public Iterator deepiterator() {
1786
        return this.layers.deepiterator();
1787
    }
1788

    
1789
    public MapTimeContext getTimeContext() {
1790
        TimeSupportManager timeSupportManager = TimeSupportLocator.getManager();
1791
        Interval interval = null;
1792
        final List<Instant> times = new ArrayList<>();
1793
        Instant minInstant = null;
1794
        Instant maxInstant = null;
1795

    
1796
        //TODO Separate absolute and relative time
1797
        FLayers layers = this.getLayers();
1798
        for( Iterator<FLayer> iterator = layers.deepiterator(); iterator.hasNext(); ) {
1799
            FLayer layer = iterator.next();
1800
            if( layer instanceof SingleLayer ) {
1801
                DataStore dataStore = ((SingleLayer) layer).getDataStore();
1802
                if( dataStore.getInterval() != null ) {
1803
                    Instant startInstant = dataStore.getInterval().getStart();
1804
                    Instant endInstant = dataStore.getInterval().getEnd();
1805
                    times.addAll(dataStore.getTimes());
1806
                    if( minInstant == null ) {
1807
                        minInstant = startInstant;
1808
                        maxInstant = endInstant;
1809
                    } else {
1810
                        if( minInstant.isAfter(startInstant) ) {
1811
                            minInstant = startInstant;
1812
                        }
1813
                        if( maxInstant.isBefore(endInstant) ) {
1814
                            maxInstant = endInstant;
1815
                        }
1816
                    }
1817
                }
1818
            }
1819
        }
1820

    
1821
        if( minInstant != null ) {
1822
            if( minInstant.isAbsolute() ) {
1823
                try {
1824
                    interval = timeSupportManager.createAbsoluteInterval((AbsoluteInstant) minInstant, (AbsoluteInstant) maxInstant);
1825
                } catch (AbsoluteIntervalTypeNotRegisteredException e) {
1826
                    LOGGER.warn("Error creating the time interval", e);
1827
                }
1828
            } else {
1829
                interval = timeSupportManager.createRelativeInterval(((RelativeInstant) minInstant).toMillis(), ((RelativeInstant) maxInstant).toMillis());
1830
            }
1831
        }
1832
        final Interval tmp_interval = interval;
1833
        return new MapTimeContext() {
1834
            @Override
1835
            public Interval getInterval() {
1836
                return tmp_interval;
1837
            }
1838

    
1839
            @Override
1840
            public List<Instant> getTimes() {
1841
                return times;
1842
            }
1843
        };
1844
    }
1845

    
1846
}