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

History | View | Annotate | Download (63.9 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.text.MessageFormat;
31
import java.util.ArrayList;
32
import java.util.Iterator;
33
import java.util.List;
34
import org.apache.commons.lang3.ArrayUtils;
35

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

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

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

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

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

    
129
    public static ArrayList AREANAMES = new ArrayList();
130
    public static ArrayList AREAABBR = new ArrayList();
131
    public static ArrayList AREATRANS2METER = new ArrayList();
132

    
133
    public static ArrayList DISTANCENAMES = new ArrayList();
134
    public static ArrayList DISTANCEABBR = new ArrayList();
135
    public static ArrayList DISTANCETRANS2METER = new ArrayList();
136

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

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

    
158
    }
159

    
160
    private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
161

    
162
    private static final MapContextManager mapContextManager = MapContextLocator
163
            .getMapContextManager();
164

    
165
    private static final Logger logger = LoggerFactory.getLogger(MapContext.class);
166

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
339
    /* Do not alter the order and the values of this array, if you need append values.*/
340
    /**
341
     * <p>
342
     * Gets the name of all distance measurements supported by
343
     * <code>MapContext</code>.</p>
344
     */
345
//        public static final String[] NAMES= {
346
//                Messages.getString("Kilometros"),
347
//                Messages.getString("Metros"),
348
//                Messages.getString("Centimetros"),
349
//                Messages.getString("Milimetros"),
350
//                Messages.getString("Millas"),
351
//                Messages.getString("Yardas"),
352
//                Messages.getString("Pies"),
353
//                Messages.getString("Pulgadas"),
354
//                Messages.getString("Grados"),
355
//        };
356
    public static final int EQUALS = 0;
357

    
358
    public static final int DISJOINT = 1;
359

    
360
    public static final int INTERSECTS = 2;
361

    
362
    public static final int TOUCHES = 3;
363

    
364
    public static final int CROSSES = 4;
365

    
366
    public static final int WITHIN = 5;
367

    
368
    public static final int CONTAINS = 6;
369

    
370
    public static final int OVERLAPS = 7;
371

    
372
    /**
373
     * A hierarchy of {@link FLayers FLayers} nodes.
374
     *
375
     * @see #getLayers()
376
     * @see #print(Graphics2D, double, PrintAttributes)
377
     */
378
    protected FLayers layers;
379

    
380
    /**
381
     * A layer with graphical items: geometries and symbols.
382
     *
383
     * @see #getGraphicsLayer()
384
     * @see #setGraphicsLayer(GraphicLayer)
385
     * @see #print(Graphics2D, double, PrintAttributes)
386
     */
387
    private GraphicLayer tracLayer = null;
388
    //MapContextLocator.getMapContextManager().createGraphicsLayer(getProjection());
389

    
390
    /**
391
     * Information for draw layers in a view.
392
     *
393
     * @see #getViewPort()
394
     * @see #setViewPort(ViewPort)
395
     */
396
    private ViewPort viewPort;
397

    
398
        // private ArrayList invalidationListeners = new ArrayList();
399
    /**
400
     * Array list with all {@link LegendListener LegendListener} registered to
401
     * this map.
402
     *
403
     * @see #addLayerListener(LegendListener)
404
     * @see #removeLayerListener(LegendListener)
405
     * @see #callLegendChanged()
406
     */
407
    private ArrayList legendListeners = new ArrayList();
408

    
409
    /**
410
     * Array list with all {@link LayerDrawingListener LayerDrawingListener}
411
     * registered to this map.
412
     *
413
     * @see #addLayerDrawingListener(LayerDrawingListener)
414
     * @see #removeLayerDrawListener(LayerDrawingListener)
415
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
416
     */
417
    private ArrayList layerDrawingListeners = new ArrayList();
418

    
419
    /**
420
     * <p>
421
     * Buffer that is used to store and eject events produced on this map:
422
     * <ul>
423
     * <li>Layer collection events.
424
     * <li>View port events.
425
     * <li>Atomic events.
426
     * <li>Layer events.
427
     * <li>Legend events on a {@link Classificable Classificable} layer.
428
     * <li>Selection events on an {@link AlphanumericData AlphanumericData} data
429
     * layer.
430
     * </ul>
431
     * </p>
432
     *
433
     * @see #addAtomicEventListener(AtomicEventListener)
434
     * @see #removeAtomicEventListener(AtomicEventListener)
435
     * @see #beginAtomicEvent()
436
     * @see #endAtomicEvent()
437
     */
438
    private EventBuffer eventBuffer = new EventBuffer();
439

    
440
    /**
441
     * Event listener for the collection of layers of this map.
442
     */
443
    private LayerEventListener layerEventListener = null;
444

    
445
    /**
446
     * List with information of all errors produced on all layers.
447
     *
448
     * @see #addLayerError(String)
449
     * @see #getLayersError()
450
     * @see #clearErrors()
451
     */
452
    private ArrayList layersError = new ArrayList();
453

    
454
    /**
455
     * Array list with all {@link ErrorListener ErrorListener} registered to
456
     * this map.
457
     *
458
     * @see #addErrorListener(ErrorListener)
459
     * @see #removeErrorListener(LegendListener)
460
     * @see #reportDriverExceptions(String, List)
461
     */
462
    private ArrayList errorListeners = new ArrayList();
463

    
464
    /**
465
     * Layer order manager decides position of layers added to this map context.
466
     */
467
    private LayerOrderManager orderManager = null;
468

    
469
        // public static ResourceBundle myResourceBundle =
470
    // ResourceBundle.getBundle("FMap");
471
    /**
472
     * <p>
473
     * Default <i>zoom in</i> factor.</p>
474
     * <p>
475
     * Doing a <i>zoom in</i> operation, decreases the focal distance and
476
     * increases the eyesight angle to the surface. This allows view an smaller
477
     * area but with the items bigger.</p>
478
     */
479
    public static double ZOOMINFACTOR = 2;
480

    
481
    /**
482
     * <p>
483
     * Default <i>zoom out</i> factor.</p>
484
     * <p>
485
     * Doing a <i>zoom out</i> operation, increases the focal distance and
486
     * decreases the eyesight angle to the surface. This allows view a bigger
487
     * area but with the items smaller.</p>
488
     */
489
    public static double ZOOMOUTFACTOR = 0.5;
490

    
491
    /**
492
     *          * Draw version of the context. It's used for know when de componend has
493
     * changed any visualization property
494
     *
495
     * @see getDrawVersion
496
     * @see updateDrawVersion
497
     */
498
    private long drawVersion = 0L;
499

    
500
    private long layersVersion = 0L;
501
    private long viewPortVersion = 0L;
502
    private long graphicsLayerVersion = 0L;
503

    
504
    /**
505
     * Object to Manage Draw of MapContext
506
     */
507
    private MapContextDrawer mapContextDrawer = null;
508

    
509
    /**
510
     * Object to Manage Draw of MapContext
511
     */
512
    private Class mapContextDrawerClass = null;
513

    
514
    /**
515
     * <p>
516
     * Color used to represent the selections.</p>
517
     */
518
    private static Color selectionColor = Color.YELLOW;
519
    private ArrayList layersToSnap = new ArrayList();
520

    
521
    /**
522
     * <p>
523
     * Gets the color used to represent the selections.</p>
524
     *
525
     * @return color used to represent the selections
526
     */
527
    public static Color getSelectionColor() {
528
        return selectionColor;
529
    }
530

    
531
    /**
532
     * <p>
533
     * Sets the color used to represent the selections.</p>
534
     *
535
     * @param selectionColor color used to represent the selections
536
     */
537
    public static void setSelectionColor(Color selectionColor) {
538
        MapContext.selectionColor = selectionColor;
539
    }
540

    
541
    /**
542
     * <p>
543
     * Creates a new map context with the drawing information defined in the
544
     * view port argument, and without layers.</p>
545
     *
546
     * @param vp information for drawing the layers of this map in the available
547
     * rectangular area according a projection
548
     */
549
    public MapContext(ViewPort vp) {
550
        this(new FLayers(), vp);
551
    }
552

    
553
    public MapContext() {
554
    }
555

    
556
    /**
557
     * <p>
558
     * Creates a new map context with the layers and the drawing information
559
     * defined in the view port arguments.</p>
560
     *
561
     * @param fLayers the initial hierarchy of nodes of layers that this map
562
     * will have
563
     * @param vp information for drawing the layers of this map in the available
564
     * rectangular area according a projection
565
     */
566
    public MapContext(FLayers fLayers, ViewPort vp) {
567
        this.layers = fLayers;
568

    
569
        layerEventListener = new LayerEventListener();
570

    
571
        if (layers != null) {
572
            layers.setMapContext(this);
573
            layers.addLayerCollectionListener(layerEventListener);
574
            layers.addLayerCollectionListener(eventBuffer);
575
        }
576

    
577
        setViewPort(vp);
578
    }
579

    
580
    /**
581
     * <p>
582
     * Reports to all driver error listeners registered of a bundle of driver
583
     * exceptions caused in the same map atomic transaction.</p>
584
     *
585
     * @param introductoryText introductory text specified by developer. If
586
     * <code>null</code>, use ""
587
     * @param driverExceptions list with a bundle of driver exceptions caught
588
     * during an atomic event
589
     *
590
     * @see #addErrorListener(ErrorListener)
591
     * @see #removeErrorListener(LegendListener)
592
     */
593
    public synchronized void reportDriverExceptions(String introductoryText,
594
            List driverExceptions) {
595
        for (int i = 0; i < errorListeners.size(); i++) {
596
            ((ErrorListener) errorListeners.get(i)).
597
                    reportDriverExceptions(introductoryText, driverExceptions);
598
        }
599
    }
600

    
601
    /**
602
     * <p>
603
     * Adds the specified legend listener (if didn't exist) to receive legend
604
     * events from this map.</p>
605
     *
606
     * @param listener the legend listener
607
     *
608
     * @see #removeLayerListener(LegendListener)
609
     * @see #callLegendChanged()
610
     */
611
    public void addLayerListener(LegendListener listener) {
612
        if (!legendListeners.contains(listener)) {
613
            legendListeners.add(listener);
614
        }
615
    }
616
        // SUGERENCIA DE PABLO
617
    //        public void addLegendListener(LegendListener listener) {
618
    //                if (!legendListeners.contains(listener))
619
    //                        legendListeners.add(listener);
620
    //        }
621

    
622
    /**
623
     * <p>
624
     * Adds the specified layer drawing listener to catch and handle drawing
625
     * events from layers of this map.</p>
626
     *
627
     * @param listener the listener to add
628
     *
629
     * @see #removeLayerDrawListener(LayerDrawingListener)
630
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
631
     */
632
    public void addLayerDrawingListener(LayerDrawingListener listener) {
633
        layerDrawingListeners.add(listener);
634
    }
635

    
636
    /**
637
     * <p>
638
     * Removes the specified layer drawing listener from this map.</p>
639
     *
640
     * @param listener the listener to remove
641
     *
642
     * @see #addLayerDrawingListener(LayerDrawingListener)
643
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
644
     */
645
    public void removeLayerDrawListener(LayerDrawingListener listener) {
646
        layerDrawingListeners.remove(listener);
647
    }
648

    
649
    /**
650
     * <p>
651
     * Adds the specified error listener to receive error events from this
652
     * map.</p>
653
     *
654
     * @param listener the listener to add
655
     *
656
     * @see #removeErrorListener(LegendListener)
657
     * @see #reportDriverExceptions(String, List)
658
     */
659
    public void addErrorListener(ErrorListener listener) {
660
        errorListeners.add(listener);
661
    }
662

    
663
    /**
664
     * <p>
665
     * Removes the specified error listener from this map.</p>
666
     *
667
     * @param listener the listener to remove
668
     *
669
     * @see #addErrorListener(ErrorListener)
670
     * @see #reportDriverExceptions(String, List)
671
     */
672
    public void removeErrorListener(LegendListener listener) {
673
        legendListeners.remove(listener);
674
    }
675

    
676
        // SUGERENCIA DE PABLO:
677
    //public void removeErrorListener(ErrorListener listener) {
678
    //        errorListeners.remove(listener);
679
    //}
680
    /**
681
     * <p>
682
     * Notifies to all legend listeners registered, that one legend has
683
     * changed.</p>
684
     * <p>
685
     * This method must be called only if it's wanted to reflect a legend
686
     * change.</p>
687
     *
688
     * @see #addLayerListener(LegendListener)
689
     * @see #removeLayerListener(LegendListener)
690
     */
691
    public synchronized void callLegendChanged() {
692
        for (int i = 0; i < legendListeners.size(); i++) {
693
            ((LegendListener) legendListeners.get(i)).legendChanged(null);
694
        }
695
        // getLayers().moveTo(0,0);
696
    }
697

    
698
    /**
699
     * <p>
700
     * Fires a layer drawing event to all
701
     * {@link LayerDrawingListener LayerDrawingListener} listeners registered,
702
     * distinguishing the kind of event.</p>
703
     *
704
     * @param e the event
705
     *
706
     * @see #addLayerDrawingListener(LayerDrawingListener)
707
     * @see #removeLayerDrawListener(LayerDrawingListener)
708
     */
709
    public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
710
        for (int i = 0; i < layerDrawingListeners.size(); i++) {
711
            LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
712
            switch (e.getEventType()) {
713
                case LayerDrawEvent.LAYER_BEFORE_DRAW:
714
                    listener.beforeLayerDraw(e);
715
                    break;
716
                case LayerDrawEvent.LAYER_AFTER_DRAW:
717
                    listener.afterLayerDraw(e);
718
                    break;
719
                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
720
                    listener.beforeGraphicLayerDraw(e);
721
                    break;
722
                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
723
                    listener.afterLayerGraphicDraw(e);
724
                    break;
725
            }
726
        }
727
        // getLayers().moveTo(0,0);
728
    }
729

    
730
    /**
731
     * <p>
732
     * Notifies to all error listeners registered, that one error has been
733
     * produced.</p>
734
     *
735
     * @param e the event with information of the error
736
     *
737
     * @see #addErrorListener(ErrorListener)
738
     * @see #removeErrorListener(LegendListener)
739
     * @see #reportDriverExceptions(String, List)
740
     */
741
    public synchronized void callNewErrorEvent(ErrorEvent e) {
742
        for (int i = 0; i < errorListeners.size(); i++) {
743
            ((ErrorListener) errorListeners.get(i)).errorThrown(e);
744
        }
745
        errorListeners.clear();
746
        // getLayers().moveTo(0,0);
747
    }
748

    
749
    /**
750
     * <p>
751
     * Removes the specified layer listener from this map.</p>
752
     *
753
     * @param listener the listener to remove
754
     *
755
     * @see #addLayerListener(LegendListener)
756
     * @see #callLegendChanged()
757
     */
758
    public void removeLayerListener(LegendListener listener) {
759
        legendListeners.remove(listener);
760
    }
761

    
762
        // SUGERENCIA DE PABLO:
763
    // public void removeLegendListener(LegendListener listener) {
764
    //         legendListeners.remove(listener);
765
    // }
766
    /**
767
     * <p>
768
     * Returns the hierarchy of {@link FLayers FLayers} nodes stored in this
769
     * map.</p>
770
     *
771
     * @return the hierarchy of nodes of layers stored in this map
772
     */
773
    public FLayers getLayers() {
774
        return layers;
775
    }
776

    
777
    /**
778
     * <p>
779
     * Draws the visible layers of this map according its view port, on the
780
     * image parameter.</p>
781
     *
782
     * @param b image with an accessible buffer of image data
783
     */
784
    public void drawLabels(BufferedImage b) {
785
    }
786

    
787
    /**
788
     * @see #redraw()
789
     */
790
    public void invalidate() {
791
        updateDrawVersion();
792
        // Small hack to let the MapControl receive an event and repaint
793
        FLayer layer;
794
        if (layers.getLayersCount() > 0) {
795
            layer = layers.getLayer(layers.getLayersCount() - 1);
796
        } else {
797
            layer = getGraphicsLayer();
798
        }
799
        LayerPositionEvent layerMovedEvent = LayerPositionEvent
800
                .createLayerMovedEvent(
801
                        layer, 0, 0);
802
        eventBuffer.layerMoved(layerMovedEvent);
803
    }
804

    
805
    /**
806
     * <p>
807
     * Prints the layers of this map using the {@link Graphics2D Graphics2D}
808
     * argument, that usually is the {@link Graphics Graphics} of the printer.
809
     * </p>
810
     *
811
     * @param g for rendering 2-dimensional shapes, text and images on the
812
     * Java(tm) platform
813
     * @param scale the scale of the view. Must be between
814
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
815
     * @param properties a set with the settings to be applied to a whole print
816
     * job and to all the documents in the print job
817
     * @throws MapContextException if there is an error getting the instance of
818
     * MapContextDrawer
819
     *
820
     * @throws ReadDriverException if fails reading with driver.
821
     *
822
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
823
     * double)
824
     */
825
    public void print(Graphics2D g, double scale,
826
            PrintAttributes properties) throws ReadException,
827
            MapContextException {
828

    
829
        CompatLocator.getGraphicsUtils().setRenderingHintsForPrinting(g);
830

    
831
        Cancellable cancel = new Cancellable() {
832
            public boolean isCanceled() {
833
                return false;
834
            }
835

    
836
            public void setCanceled(boolean canceled) {
837
                // No queremos que se pueda cancelar la impresi?n.
838

    
839
            }
840
        };
841
        this.getMapContextDrawer().print(this.layers, g, cancel, scale, properties);
842
        if (tracLayer != null) {
843
            tracLayer.draw(null, g, viewPort, cancel, scale);
844
        }
845
    }
846

    
847
    /**
848
     * <p>
849
     * Returns a new <code>MapContext</code> instance with the information of
850
     * the <code>vp</code> argument, and the layers of this map.</p>
851
     *
852
     * @param vp information for drawing the layers of this map in the available
853
     * rectangular area according a projection
854
     *
855
     * @return a new <code>MapContext</code> instance projected by
856
     * <code>vp</code>
857
     */
858
    public MapContext createNewFMap(ViewPort vp) {
859
        MapContext ret = new MapContext(vp);
860
        ret.layers = this.layers;
861

    
862
        return ret;
863
    }
864

    
865
    /**
866
     * <p>
867
     * Creates a new independent <code>MapContext</code> instance, that has a
868
     * clone of the layers and the view port of this one.</p>
869
     * <p>
870
     * The new map will have the same data source drivers to avoid waste memory,
871
     * and work faster.</p>
872
     *
873
     * @return the new <code>MapContext</code> instance
874
     *
875
     * @throws XMLException if fails cloning the view port or a layer
876
     *
877
     * @see FLayer#cloneLayer()
878
     * @see ViewPort#cloneViewPort()
879
     */
880
    public MapContext cloneFMap() {
881
        ViewPort vp;
882
        try {
883
            vp = (ViewPort) getViewPort().clone();
884
        } catch (CloneNotSupportedException e) {
885
            throw new RuntimeException(e);
886
        }
887
        FLayers antLayers = getLayers();
888
        MapContext ret = new MapContext(vp);
889

    
890
        /*
891
         * Cloning order manager
892
         */
893
        LayerOrderManager lom = this.getOrderManager();
894
        try {
895
            lom = (LayerOrderManager) lom.clone();
896
            ret.setOrderManager(lom);
897
        } catch (CloneNotSupportedException e1) {
898
            logger.error("While cloning order manager", e1);
899
        }
900

    
901
        FLayers aux = new FLayers();//(ret,null);
902
        aux.setMapContext(ret);
903
        for (int i = 0; i < antLayers.getLayersCount(); i++) {
904
            FLayer lyr = antLayers.getLayer(i);
905
            try {
906
                FLayer auxLayer = lyr.cloneLayer();
907
                aux.addLayer(auxLayer);
908
                auxLayer.dispose();
909
            } catch (Exception e) {
910
                throw new RuntimeException(e);
911
            }
912
        }
913
        ret.layers = aux;
914
        return ret;
915

    
916
    }
917

    
918
    /**
919
     * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather
920
     * copies them.
921
     *
922
     * @return the new map
923
     */
924
    public MapContext cloneToDraw() {
925
        ViewPort vp;
926
        try {
927
            vp = (ViewPort) getViewPort().clone();
928
            MapContext mapContext = new MapContext(getLayers(), vp);
929
            return mapContext;
930
        } catch (CloneNotSupportedException e) {
931
            throw new RuntimeException(e);
932
        }
933
    }
934

    
935
    /**
936
     * <p>
937
     * Adds a layer to the group of layers that are at a upper level in the
938
     * tree.</p>
939
     *
940
     * @param vectorial the layer to add
941
     */
942
    public void addToTrackLayer(FLayer vectorial) {
943
    }
944

    
945
    /**
946
     * <p>
947
     * Returns the scale of the view in the screen.</p>
948
     *
949
     * @return one of this values:
950
     * <ul>
951
     * <li>the scale of the adjusted extent scale of the view in the screen
952
     * <li><code>-1</code> if there is no image
953
     * <li><code>0</code> if there is no extent defined for the image
954
     * </ul>
955
     *
956
     * @see #setScaleView(long)
957
     * @see ViewPort#getAdjustedExtent()
958
     * @see IProjection#getScale(double, double, double, double)
959
     */
960
    public long getScaleView() {
961
        double dpi = this.getViewPort().getDPI();
962
        IProjection proj = viewPort.getProjection();
963

    
964
        if (viewPort.getImageSize() == null) {
965
            return -1;
966
        }
967

    
968
        if (viewPort.getAdjustedEnvelope() == null) {
969
            return 0;
970
        }
971
        double[] trans2Meter = getDistanceTrans2Meter();
972
        int mUnits = getViewPort().getMapUnits();
973

    
974
        if (proj == null) {
975
            double w = ((viewPort.getImageSize().width / dpi) * 0.0254);
976
            return (long) (viewPort.getAdjustedEnvelope().getLength(0)
977
                    / w * trans2Meter[mUnits]);
978
        }
979

    
980
        return Math.round(proj.getScale(
981
                viewPort.getAdjustedEnvelope().getMinimum(0) * trans2Meter[mUnits],
982
                viewPort.getAdjustedEnvelope().getMaximum(0) * trans2Meter[mUnits],
983
                viewPort.getImageSize().width,
984
                dpi));
985

    
986
    }
987

    
988
    /**
989
     * <p>
990
     * Sets the new extent of the view, calculated using the scale argument.</p>
991
     * <p>
992
     * Doesn't updates the scale if there isn't information about the dimension
993
     * of the image or the adjusted extent.</p>
994
     *
995
     * @param scale the new scale for the view
996
     *
997
     * @see ViewPort#setProjection(IProjection)
998
     * @see #getScaleView()
999
     */
1000
    public void setScaleView(long scale) {
1001
        double dpi = this.getViewPort().getDPI();
1002
        if (viewPort.getImageSize() == null) {
1003
            return;
1004
        }
1005
        IProjection proj = viewPort.getProjection();
1006
        if (viewPort.getAdjustedExtent() == null) {
1007
            return;
1008
        }
1009
        double[] trans2Meter = getDistanceTrans2Meter();
1010
        Envelope env = viewPort.getAdjustedExtent();
1011
        Rectangle2D r = new Rectangle2D.Double(env.getMinimum(0), env.getMinimum(1), env.getLength(0), env.getLength(1));
1012
        Rectangle2D rec = proj.getExtent(r, scale, viewPort.getImageWidth(), viewPort.getImageHeight(), 100 * getDistanceTrans2Meter()[getViewPort().getMapUnits()], trans2Meter[getViewPort().getDistanceUnits()], dpi);
1013
        try {
1014
            getViewPort().setEnvelope(geomManager.createEnvelope(rec.getX(), rec.getY(), rec.getMaxX(), rec.getMaxY(), SUBTYPES.GEOM2D));
1015
        } catch (CreateEnvelopeException e) {
1016
            logger.error("Error seting the bounding box");
1017
        }
1018
    }
1019

    
1020
    /**
1021
     * <p>
1022
     * Returns the screen resolution (Dots Per Inch) as it was defined by the
1023
     * user's preference, or by default as it is defined in the default
1024
     * Toolkit.</p>
1025
     *
1026
     * Be care, use ViewPort#getDPI to ensure are using the corrects DPI.
1027
     *
1028
     * @return double with the screen's dpi
1029
     */
1030
    public static double getScreenDPI() {
1031
        return CompatLocator.getGraphicsUtils().getScreenDPI();
1032
    }
1033

    
1034
    /**
1035
     * @see
1036
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
1037
     */
1038
    public void process(Visitor visitor) {
1039
    }
1040

    
1041
    /**
1042
     * @see
1043
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
1044
     */
1045
    public void processSelected(Visitor visitor) {
1046
    }
1047

    
1048
    /**
1049
     * @see
1050
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
1051
     * VectorialSubSet)
1052
     */
1053
    public void select(Visitor visitor) {
1054
    }
1055

    
1056
    /**
1057
     * @see
1058
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
1059
     */
1060
    public void selectFromSelection() {
1061
    }
1062

    
1063
    /**
1064
     * @see
1065
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
1066
     */
1067
    public void createIndex() {
1068
    }
1069

    
1070
    /**
1071
     * @see org.cresques.geo.Projected#getProjection()
1072
     *
1073
     * @see ViewPort#getProjection()
1074
     * @see #setProjection(IProjection)
1075
     * @see #reProject(ICoordTrans)
1076
     */
1077
    public IProjection getProjection() {
1078
        return getViewPort().getProjection();
1079
    }
1080

    
1081
    /**
1082
     * <p>
1083
     * Sets the new projection.</p>
1084
     *
1085
     * @param proj the new projection
1086
     *
1087
     * @see #getProjection()
1088
     * @see ViewPort#setProjection(IProjection)
1089
     * @see #reProject(ICoordTrans)
1090
     */
1091
    public void setProjection(IProjection proj) {
1092
        if (getViewPort() != null) {
1093
            getViewPort().setProjection(proj);
1094
        }
1095
    }
1096

    
1097
    /**
1098
     * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
1099
     */
1100
    public void reProject(ICoordTrans arg0) {
1101
        // TODO implementar reprojecci?n (lo que sea eso)
1102
    }
1103

    
1104
    public Envelope getSelectionBounds() throws BaseException {
1105

    
1106
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1107

    
1108
        layers.accept(visitor);
1109
        Envelope env_in_data_crs = visitor.getSelectioEnvelope();
1110
        return env_in_data_crs;
1111
    }
1112

    
1113
    /**
1114
     * <p>
1115
     * Draws this map if its {@link ViewPort ViewPort} has an extent
1116
     * defined:<br>
1117
     * <ol>
1118
     * <li>Selects only the layers that have to be drawn:
1119
     * {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
1120
     * <li>Sets quality: antialiasing by text and images, and quality rendering.
1121
     * <li>Draws the layers.
1122
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
1123
     * <li>Draws the graphic layer.
1124
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
1125
     * <li>Invokes the garbage collector and memory clean.
1126
     * </ol>
1127
     * </p>
1128
     *
1129
     * @param image buffer used sometimes instead <code>g</code> to accelerate
1130
     * the draw. For example, if two points are as closed that can't be
1131
     * distinguished, draws only one.
1132
     * @param g for rendering 2-dimensional shapes, text and images on the
1133
     * Java(tm) platform
1134
     * @param cancel shared object that determines if this layer can continue
1135
     * being drawn
1136
     * @param scale the scale of the view. Must be between
1137
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1138
     * @throws MapContextException if there is an error getting the instance of
1139
     * MapContextDrawer
1140
     * @throws ReadDriverException if fails reading with the driver.
1141
     */
1142
    public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
1143
            double scale) throws ReadException, MapContextException {
1144
        if (viewPort.getEnvelope() == null) {
1145
            return;
1146
        }
1147

    
1148
        CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1149

    
1150
        this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1151
    }
1152

    
1153
    /**
1154
     * <p>
1155
     * Draws only the internal graphic layer using the information of the
1156
     * {@link ViewPort ViewPort} of this map.</p>
1157
     *
1158
     * @param image image used to accelerate the screen draw
1159
     * @param g for rendering 2-dimensional shapes, text and images on the
1160
     * Java(tm) platform
1161
     * @param cancel shared object that determines if this layer can continue
1162
     * being drawn
1163
     * @param scale value that represents the scale
1164
     * @throws ReadDriverException if fails reading with the driver.
1165
     * @deprecated use
1166
     * {@link #draw(BufferedImage, Graphics2D, Cancellable, double)} instead
1167
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
1168
     * double)
1169
     */
1170
    public void drawGraphics(BufferedImage image, Graphics2D g,
1171
            Cancellable cancel, double scale) throws ReadException {
1172

    
1173
                // From now on the graphics layer is handled by the MapContextDrawer,
1174
        // so call the draw method instead.
1175
        try {
1176
            draw(image, g, cancel, scale);
1177
        } catch (MapContextException e) {
1178
            throw new RuntimeException(e);
1179
        }
1180
    }
1181

    
1182
    /**
1183
     * <p>
1184
     * Like
1185
     * {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}
1186
     * , but creating the task as cancellable.
1187
     * </p>
1188
     *
1189
     * @param image buffer used sometimes instead <code>g</code> to accelerate
1190
     * the draw. For example, if two points are as closed that can't be
1191
     * distinguished, draws only one.
1192
     * @param g for rendering 2-dimensional shapes, text and images on the
1193
     * Java(tm) platform
1194
     * @param scale the scale of the view. Must be between
1195
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1196
     * @throws MapContextException if there is an error getting the instance of
1197
     * MapContextDrawer
1198
     *
1199
     * @throws ReadDriverException if the driver fails reading.
1200
     *
1201
     * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1202
     */
1203
    public void draw(BufferedImage image, Graphics2D g, double scale)
1204
            throws ReadException, MapContextException {
1205
        draw(image, g, new Cancellable() {
1206
            /**
1207
             * @see org.gvsig.utils.swing.threads.Cancellable#isCanceled()
1208
             */
1209
            public boolean isCanceled() {
1210
                return false;
1211
            }
1212

    
1213
            public void setCanceled(boolean canceled) {
1214
                // Do nothing
1215
            }
1216
        }, scale);
1217
    }
1218

    
1219
    /**
1220
     * <p>
1221
     * Gets the {@link ViewPort ViewPort} associated to this map.</p>
1222
     *
1223
     * @return the view port
1224
     *
1225
     * @see #setViewPort(ViewPort)
1226
     */
1227
    public ViewPort getViewPort() {
1228
        return viewPort;
1229
    }
1230

    
1231
    /**
1232
     * <p>
1233
     * Sets a {@link ViewPort ViewPort} with the drawing information of this
1234
     * map.</p>
1235
     * <p>
1236
     * If there was a previous view port, removes its
1237
     * {@link EventBuffer EventBuffer} and adds the new one.</p>
1238
     *
1239
     * @param viewPort the viewPort
1240
     *
1241
     * @see #getViewPort()
1242
     */
1243
    public void setViewPort(ViewPort viewPort) {
1244
        if (this.viewPort != null) {
1245
            this.viewPort.removeViewPortListener(eventBuffer);
1246
        }
1247

    
1248
        if (this.mapContextDrawer != null) {
1249
            this.mapContextDrawer.setViewPort(viewPort);
1250
        }
1251

    
1252
        this.viewPort = viewPort;
1253
        if (viewPort != null) {
1254
            viewPort.addViewPortListener(eventBuffer);
1255
        }
1256
    }
1257

    
1258
    /**
1259
     * <p>
1260
     * Sets the given extent to the {@link ViewPort ViewPort} and updates the
1261
     * view with the new zoom.</p>
1262
     *
1263
     * @param extent the extent of the new zoom
1264
     */
1265
    public void zoomToEnvelope(Envelope extent) {
1266
        if (extent != null && !extent.isEmpty()) {
1267
            getViewPort().setEnvelope(extent);
1268
        }
1269
    }
1270

    
1271
    /**
1272
     * <p>
1273
     * Returns the union of all extents of all layers of this map.</p>
1274
     *
1275
     * @return full extent of layers of this map
1276
     * @throws ReadDriverException if the driver fails reading.
1277
     *
1278
     * @see FLayers#getFullEnvelope()
1279
     */
1280
    public Envelope getFullEnvelope() throws ReadException {
1281
        Envelope envelope = layers.getFullEnvelope();
1282

    
1283
        if (tracLayer != null) {
1284
            Envelope graphicsEnvelope = tracLayer.getFullEnvelope();
1285
            if (envelope == null) {
1286
                return graphicsEnvelope;
1287
            } else if (graphicsEnvelope != null) {
1288
                envelope.add(graphicsEnvelope);
1289
            }
1290
        }
1291

    
1292
        return envelope;
1293
    }
1294

    
1295
    /**
1296
     * <p>
1297
     * Adds a listener of atomic events to the internal
1298
     * {@link EventBuffer EventBuffer}.</p>
1299
     *
1300
     * @param listener the new listener
1301
     *
1302
     * @return <code>true</code> if has added the listener successfully
1303
     *
1304
     * @see #removeAtomicEventListener(AtomicEventListener)
1305
     * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1306
     */
1307
    public boolean addAtomicEventListener(AtomicEventListener listener) {
1308
        return eventBuffer.addAtomicEventListener(listener);
1309
    }
1310

    
1311
    /**
1312
     * <p>
1313
     * Removes a listener of atomic events from the internal
1314
     * {@link EventBuffer EventBuffer}.</p>
1315
     *
1316
     * @param listener the listener to remove
1317
     *
1318
     * @return <tt>true</tt> if the list contained the specified element
1319
     *
1320
     * @see #addAtomicEventListener(AtomicEventListener)
1321
     * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1322
     */
1323
    public boolean removeAtomicEventListener(AtomicEventListener listener) {
1324
        return eventBuffer.removeAtomicEventListener(listener);
1325
    }
1326

    
1327
    /**
1328
     * @see EventBuffer#beginAtomicEvent()
1329
     *
1330
     * @see #endAtomicEvent()
1331
     */
1332
    public void beginAtomicEvent() {
1333
        eventBuffer.beginAtomicEvent();
1334
    }
1335

    
1336
    /**
1337
     * @see EventBuffer#endAtomicEvent()
1338
     *
1339
     * @see #beginAtomicEvent()
1340
     */
1341
    public void endAtomicEvent() {
1342
        eventBuffer.endAtomicEvent();
1343
    }
1344

    
1345
    /**
1346
     * <p>
1347
     * The class <code>LayerEventListener</code> implements the methods of
1348
     * {@link LayerCollectionListener LayerCollectionListener} that handles the
1349
     * "layer added" or "layer removed" events in a map.</p>
1350
     * <p>
1351
     * Is designed as a listener for all layers in a
1352
     * {@link MapContext MapContext}.</p>
1353
     *
1354
     * @author Fernando Gonz?lez Cort?s
1355
     */
1356
    public class LayerEventListener implements LayerCollectionListener {
1357
        /*
1358
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1359
         */
1360

    
1361
        public void layerAdded(LayerCollectionEvent e) {
1362
                        // Si es la primera capa, fijamos su extent al ViewPort
1363
            // if (getLayers().getLayersCount() == 1) {
1364
            if (getViewPort().getEnvelope() == null) {
1365
                FLayer lyr = e.getAffectedLayer();
1366
                if (lyr.isAvailable()) {
1367
                    try {
1368
                        getViewPort().setEnvelope(lyr.getFullEnvelope());
1369
                    } catch (Exception ex) {
1370
                        logger.warn(
1371
                                MessageFormat.format(
1372
                                    "Can''t set envelope to view port from layer ''{0}''",
1373
                                    new Object[]{lyr.getName()}
1374
                                ),
1375
                                ex
1376
                        );
1377
                    }
1378
                }
1379
            }
1380

    
1381
            // Registramos al FMap como listener del legend de las capas
1382
            FLayer lyr = e.getAffectedLayer();
1383
            addSelectionListener(lyr);
1384
        }
1385

    
1386
        /*
1387
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1388
         */
1389
        public void layerMoved(LayerPositionEvent e) {
1390
        }
1391

    
1392
        /*
1393
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1394
         */
1395
        public void layerRemoved(LayerCollectionEvent e) {
1396
            FLayer lyr = e.getAffectedLayer();
1397

    
1398
            lyr.removeLayerListener(eventBuffer);
1399

    
1400
            if (lyr instanceof Classifiable) {
1401
                Classifiable c = (Classifiable) lyr;
1402
                c.removeLegendListener(eventBuffer);
1403
            }
1404

    
1405
            if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore() != null) {
1406
                ((SingleLayer) lyr).getDataStore().deleteObserver(
1407
                        MapContext.this);
1408
            }
1409

    
1410
        }
1411

    
1412
        /*
1413
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1414
         */
1415
        public void layerAdding(LayerCollectionEvent e)
1416
                throws CancelationException {
1417
        }
1418

    
1419
        /*
1420
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1421
         */
1422
        public void layerMoving(LayerPositionEvent e)
1423
                throws CancelationException {
1424
        }
1425

    
1426
        /*
1427
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1428
         */
1429
        public void layerRemoving(LayerCollectionEvent e)
1430
                throws CancelationException {
1431
        }
1432

    
1433

    
1434
        /*
1435
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1436
         */
1437
        public void visibilityChanged(LayerCollectionEvent e)
1438
                throws CancelationException {
1439
        }
1440
    }
1441

    
1442
    /**
1443
     * <p>
1444
     * Adds the {@link LayerEventListener LayerEventListener} of this map to the
1445
     * collection of layers argument.</p>
1446
     *
1447
     * @param a collection of layers
1448
     */
1449
    public void addAsCollectionListener(FLayers layers2) {
1450
        layers2.addLayerCollectionListener(layerEventListener);
1451
    }
1452

    
1453
    /**
1454
     * <p>
1455
     * Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1456
     *
1457
     * @return the graphic layer of this map
1458
     *
1459
     * @see #setGraphicsLayer(GraphicLayer)
1460
     */
1461
    public GraphicLayer getGraphicsLayer() {
1462
        if (tracLayer == null) {
1463
            if (getViewPort() != null) {
1464
                this.tracLayer
1465
                        = MapContextLocator.getMapContextManager()
1466
                        .createGraphicsLayer(
1467
                                getViewPort().getProjection());
1468
            } else {
1469
                this.tracLayer
1470
                        = MapContextLocator.getMapContextManager()
1471
                        .createGraphicsLayer(null);
1472
            }
1473
        }
1474
        return tracLayer;
1475
    }
1476

    
1477
    /**
1478
     * <p>
1479
     * Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1480
     *
1481
     * @param graphicLayer the new graphic layer
1482
     *
1483
     * @see #getGraphicsLayer()
1484
     */
1485
    public void setGraphicsLayer(GraphicLayer graphicLayer) {
1486
        tracLayer = graphicLayer;
1487
    }
1488

    
1489
    /**
1490
     * <p>
1491
     * Indicates whether some other object is "equal to" this map.</p>
1492
     * <p>
1493
     * Returns <code>true</code> if success one of this options:
1494
     * <ul>
1495
     * <li>Both objects are equal according to
1496
     * {@linkplain Object#equals(Object)}.
1497
     * <li>Both maps have the same layers.
1498
     * <li>Both maps have the same number of layers and with the same name.
1499
     * </ul>
1500
     * </p>
1501
     *
1502
     * @param obj the reference object with which to compare.
1503
     * @return <code>true</code> if this object is the same as the
1504
     * <code>arg0</code> argument; otherwise <code>false</code>.
1505
     *
1506
     * @see Object#equals(Object)
1507
     */
1508
    public boolean equals(Object arg0) {
1509
        if (!(arg0 instanceof MapContext)) {
1510
            return false;
1511
        }
1512
        MapContext map = (MapContext) arg0;
1513
        if (super.equals(arg0)) {
1514
            return true;
1515
        }
1516
        if (getLayers() == map.getLayers()) {
1517
            return true;
1518
        }
1519
        boolean isEqual = true;
1520
        if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1521
            for (int i = 0; i < getLayers().getLayersCount(); i++) {
1522

    
1523
                if (!getLayers().getLayer(i).getName().equals(
1524
                        map.getLayers().getLayer(i).getName())) {
1525
                    isEqual = false;
1526
                }
1527

    
1528
            }
1529
        } else {
1530
            isEqual = false;
1531
        }
1532
        return isEqual;
1533
    }
1534

    
1535
    /**
1536
     * <p>
1537
     * Registers the message of an error associated to this map.</p>
1538
     *
1539
     * @param stringProperty the error message
1540
     *
1541
     * @see #getLayersError()
1542
     * @see #clearErrors()
1543
     */
1544
    public void addLayerError(String stringProperty) {
1545
        layersError.add(stringProperty);
1546
    }
1547

    
1548
    /**
1549
     * <p>
1550
     * Gets the list with all error messages registered to this map.</p>
1551
     *
1552
     * @return the list of errors registered to this map
1553
     *
1554
     * @see #addLayerError(String)
1555
     * @see #clearErrors()
1556
     */
1557
    public ArrayList getLayersError() {
1558
        return layersError;
1559
    }
1560

    
1561
    /**
1562
     * <p>
1563
     * Removes all error messages associated to this map.</p>
1564
     *
1565
     * @see #addLayerError(String)
1566
     * @see #getLayersError()
1567
     */
1568
    public void clearErrors() {
1569
        layersError.clear();
1570
    }
1571

    
1572
    /**
1573
     * <p>
1574
     * Creates and returns a new group of layers that belongs to this
1575
     * <code>MapContext</code>.</p>
1576
     *
1577
     * @param parent layer node in this <code>MapContexte</code> that will be
1578
     * the parent of the new node
1579
     * @return the new layer node
1580
     */
1581
    public FLayers getNewGroupLayer(FLayers parent) {
1582
        FLayers group1 = new FLayers();//(this,parent);
1583
        group1.setMapContext(this);
1584
        group1.setParentLayer(parent);
1585
        return group1;
1586
    }
1587

    
1588
    public String getClassName() {
1589
        return null;
1590
    }
1591

    
1592
    public ArrayList getLayersToSnap() {
1593
        return layersToSnap;
1594
    }
1595

    
1596
    public void setLayersToSnap(ArrayList layersToSnap) {
1597
        this.layersToSnap = layersToSnap;
1598

    
1599
    }
1600

    
1601
    public void update(Observable observable, Object notification) {
1602
        // TODO REVISAR ESTO!!!
1603
        String ntype = null;
1604
        if (notification instanceof FeatureStoreNotification) {
1605
            FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1606
            ntype = fsNotification.getType();
1607
            if (ntype.equals(FeatureStoreNotification.LOAD_FINISHED)
1608
                    || ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)) {
1609
                getLayers().moveTo(0, 0);
1610
            }
1611
        }
1612
    }
1613

    
1614
    public long getDrawVersion() {
1615
        if (getViewPort().getDrawVersion() > this.viewPortVersion
1616
                || getLayers().getDrawVersion() > this.layersVersion
1617
                || getGraphicsLayer().getDrawVersion() > graphicsLayerVersion) {
1618
            updateDrawVersion();
1619
        }
1620
        return this.drawVersion;
1621
    }
1622

    
1623
    protected void updateDrawVersion() {
1624
        this.layersVersion = getLayers().getDrawVersion();
1625
        this.viewPortVersion = getViewPort().getDrawVersion();
1626
        this.graphicsLayerVersion = getGraphicsLayer().getDrawVersion();
1627
        this.drawVersion++;
1628
    }
1629

    
1630
    public MapContextDrawer getMapContextDrawer() throws ReadException,
1631
            MapContextException {
1632
        if (this.mapContextDrawer == null) {
1633
            if (mapContextDrawerClass == null) {
1634
                this.mapContextDrawer = mapContextManager
1635
                        .createDefaultMapContextDrawerInstance();
1636
            } else {
1637
                this.mapContextDrawer = mapContextManager
1638
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1639
            }
1640
            this.mapContextDrawer.setMapContext(this);
1641
            this.mapContextDrawer.setViewPort(viewPort);
1642
        }
1643

    
1644
        return this.mapContextDrawer;
1645
    }
1646

    
1647
    public void setMapContextDrawerClass(Class mapContextDrawerClass)
1648
            throws MapContextException {
1649
        mapContextManager.validateMapContextDrawer(mapContextDrawerClass);
1650
        this.mapContextDrawerClass = mapContextDrawerClass;
1651
        if (this.mapContextDrawer != null) {
1652
            this.mapContextDrawer.dispose();
1653
            this.mapContextDrawer = null;
1654
        }
1655
    }
1656

    
1657
    public void setMapContextDrawer(MapContextDrawer drawer) {
1658
        if (this.mapContextDrawer != null) {
1659
            this.mapContextDrawer.dispose();
1660
            this.mapContextDrawer = null;
1661
        }
1662
        this.mapContextDrawer = drawer;
1663
        if (this.mapContextDrawer != null) {
1664
            this.mapContextDrawer.setMapContext(this);
1665
            this.mapContextDrawer.setViewPort(viewPort);
1666
        }
1667
    }
1668

    
1669
    public void loadFromState(PersistentState state)
1670
            throws PersistenceException {
1671

    
1672
        ViewPort vp = (ViewPort) state.get("ViewPort");
1673
        setViewPort(vp);
1674

    
1675
        layers = (FLayers) state.get("layers");
1676
        layers.setName("root layer");
1677
        loadLayers(layers);
1678
        layers.setMapContext(this);
1679

    
1680
        layerEventListener = new LayerEventListener();
1681
        layers.addLayerCollectionListener(layerEventListener);
1682

    
1683
        layers.addLayerCollectionListener(eventBuffer);
1684
        layers.setProjection(vp.getProjection());
1685

    
1686
        //Add the listener for the selection
1687
        addSelectionListener(layers);
1688

    
1689
        // ======================
1690
        if (state.hasValue("orderManager")) {
1691
            LayerOrderManager lom = (LayerOrderManager) state.get("orderManager");
1692
            this.setOrderManager(lom);
1693
        }
1694
        DefaultMapContextManager manager = (DefaultMapContextManager) MapContextLocator.getMapContextManager();
1695
        manager.notifyLoadMapContext(this);
1696
    }
1697

    
1698
    private void loadLayers(FLayers lyrs) {
1699

    
1700
        int sz = lyrs.getLayersCount();
1701
        for (int i = 0; i < sz; i++) {
1702
            try {
1703
                lyrs.getLayer(i).load();
1704
            } catch (LoadLayerException e) {
1705
                logger.error("While loading layer: " + lyrs.getLayer(i).getName());
1706
            }
1707
        }
1708
    }
1709

    
1710
    public void saveToState(PersistentState state) throws PersistenceException {
1711
        state.set("ViewPort", viewPort);
1712
        state.set("layers", (Persistent)layers);
1713
        state.set("orderManager", this.getOrderManager());
1714
    }
1715

    
1716
    public static class RegisterPersistence implements Callable {
1717

    
1718
        public Object call() {
1719
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1720
            DynStruct definition = manager.addDefinition(
1721
                    MapContext.class,
1722
                    "MapContext",
1723
                    "MapContext Persistence definition",
1724
                    null,
1725
                    null
1726
            );
1727
            definition.addDynFieldObject("ViewPort")
1728
                    .setClassOfValue(ViewPort.class)
1729
                    .setMandatory(true);
1730

    
1731
            definition.addDynFieldObject("layers")
1732
                    .setClassOfValue(FLayers.class)
1733
                    .setMandatory(true);
1734

    
1735
            definition.addDynFieldObject("orderManager")
1736
                    .setClassOfValue(LayerOrderManager.class)
1737
                    .setMandatory(false);
1738

    
1739
            return Boolean.TRUE;
1740
        }
1741
    }
1742

    
1743
    protected void doDispose() throws BaseException {
1744
        dispose(layers);
1745
        dispose(tracLayer);
1746
    }
1747

    
1748
    /**
1749
     * <p>
1750
     * Registers an event buffer as a listener for all layers as argument.</p>
1751
     *
1752
     * <p>
1753
     * Each {@link FLayer FLayer} of this map must have an event buffer for all
1754
     * kind of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1755
     * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers}
1756
     * layers, and for each one, registers, for their specific listeners, the
1757
     * <code>eventBuffer</code> as a listener.</p>
1758
     *
1759
     * @param the layer or layers
1760
     */
1761
    private void addSelectionListener(FLayer lyr) {
1762
        lyr.addLayerListener(eventBuffer);
1763

    
1764
        if (lyr instanceof Classifiable) {
1765
            Classifiable c = (Classifiable) lyr;
1766
            c.addLegendListener(eventBuffer);
1767
        }
1768

    
1769
        if (lyr instanceof FLayers) {
1770
            FLayers lyrs = (FLayers) lyr;
1771
            for (int i = 0; i < lyrs.getLayersCount(); i++) {
1772
                addSelectionListener(lyrs.getLayer(i));
1773
            }
1774
        }
1775
        if (lyr instanceof SingleLayer) {
1776
            if (((SingleLayer) lyr).getDataStore() != null) {
1777
                ((SingleLayer) lyr).getDataStore().addObserver(
1778
                        MapContext.this);
1779
            }
1780
        }
1781
    }
1782

    
1783
    public void setOrderManager(LayerOrderManager lom) {
1784
        orderManager = lom;
1785
    }
1786

    
1787
    public LayerOrderManager getOrderManager() {
1788

    
1789
        if (orderManager == null) {
1790
            orderManager = MapContextLocator.getDefaultOrderManager();
1791
        }
1792
        return orderManager;
1793
    }
1794

    
1795
    public boolean hasVectorLayers() {
1796
        return this.hasVectorLayers(this.getLayers());
1797
    }
1798

    
1799
    public boolean hasActiveVectorLayers() {
1800
        FLayer[] layers = this.getLayers().getActives();
1801
        for (int i = 0; i < layers.length; i++) {
1802
            FLayer layer = layers[i];
1803
            if (layer.isAvailable() && layer instanceof FLyrVect) {
1804
                return true;
1805
            }
1806
        }
1807
        return false;
1808
    }
1809

    
1810
    public boolean hasActiveLayers() {
1811
        FLayer[] layers = this.getLayers().getActives();
1812
        return !ArrayUtils.isEmpty(layers);
1813
    }
1814

    
1815
    public boolean hasLayers() {
1816
        return !this.getLayers().isEmpty();
1817
    }
1818
    
1819
    private boolean hasVectorLayers(FLayers layers) {
1820
        for (int i = 0; i < layers.getLayersCount(); i++) {
1821
            FLayer lyr = layers.getLayer(i);
1822
            if (lyr instanceof FLayers) {
1823
                if (hasVectorLayers((FLayers) lyr)) {
1824
                    return true;
1825
                }
1826
            } else if (lyr instanceof FLyrVect) {
1827
                return true;
1828
            }
1829
        }
1830
        return false;
1831
    }
1832
    
1833
    @Override
1834
    public Iterator<FLayer> iterator() {
1835
        return this.layers.iterator();
1836
    }
1837

    
1838
    public Iterator deepiterator() {
1839
        return this.layers.deepiterator();
1840
    }
1841

    
1842
    public MapTimeContext getTimeContext() {
1843
        TimeSupportManager timeSupportManager = TimeSupportLocator.getManager();
1844
        Interval interval = null;
1845
        final List<Instant> times = new ArrayList<>();
1846
        Instant minInstant = null;
1847
        Instant maxInstant = null;
1848

    
1849
        //TODO Separate absolute and relative time
1850
        FLayers layers = this.getLayers();
1851
        for( Iterator<FLayer> iterator = layers.deepiterator(); iterator.hasNext(); ) {
1852
            FLayer layer = iterator.next();
1853
            if( layer instanceof SingleLayer ) {
1854
                DataStore dataStore = ((SingleLayer) layer).getDataStore();
1855
                if( dataStore.getInterval() != null ) {
1856
                    Instant startInstant = dataStore.getInterval().getStart();
1857
                    Instant endInstant = dataStore.getInterval().getEnd();
1858
                    times.addAll(dataStore.getTimes());
1859
                    if( minInstant == null ) {
1860
                        minInstant = startInstant;
1861
                        maxInstant = endInstant;
1862
                    } else {
1863
                        if( minInstant.isAfter(startInstant) ) {
1864
                            minInstant = startInstant;
1865
                        }
1866
                        if( maxInstant.isBefore(endInstant) ) {
1867
                            maxInstant = endInstant;
1868
                        }
1869
                    }
1870
                }
1871
            }
1872
        }
1873

    
1874
        if( minInstant != null ) {
1875
            if( minInstant.isAbsolute() ) {
1876
                try {
1877
                    interval = timeSupportManager.createAbsoluteInterval((AbsoluteInstant) minInstant, (AbsoluteInstant) maxInstant);
1878
                } catch (AbsoluteIntervalTypeNotRegisteredException e) {
1879
                    logger.warn("Error creating the time interval", e);
1880
                }
1881
            } else {
1882
                interval = timeSupportManager.createRelativeInterval(((RelativeInstant) minInstant).toMillis(), ((RelativeInstant) maxInstant).toMillis());
1883
            }
1884
        }
1885
        final Interval tmp_interval = interval;
1886
        return new MapTimeContext() {
1887
            @Override
1888
            public Interval getInterval() {
1889
                return tmp_interval;
1890
            }
1891

    
1892
            @Override
1893
            public List<Instant> getTimes() {
1894
                return times;
1895
            }
1896
        };
1897
    }
1898
    
1899
}