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

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

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

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

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

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

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

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

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

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

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

    
159
    }
160

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

    
163
    private static final MapContextManager mapContextManager = MapContextLocator
164
            .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 drawFrameRate 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 dFR) {
211
        drawFrameRate = dFR;
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(new Double(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)).doubleValue();
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(new Double(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)).doubleValue();
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
    /* Do not alter the order and the values of this array, if you need append values.*/
341
    /**
342
     * <p>
343
     * Gets the name of all distance measurements supported by
344
     * <code>MapContext</code>.</p>
345
     */
346
//        public static final String[] NAMES= {
347
//                Messages.getString("Kilometros"),
348
//                Messages.getString("Metros"),
349
//                Messages.getString("Centimetros"),
350
//                Messages.getString("Milimetros"),
351
//                Messages.getString("Millas"),
352
//                Messages.getString("Yardas"),
353
//                Messages.getString("Pies"),
354
//                Messages.getString("Pulgadas"),
355
//                Messages.getString("Grados"),
356
//        };
357
    public static final int EQUALS = 0;
358

    
359
    public static final int DISJOINT = 1;
360

    
361
    public static final int INTERSECTS = 2;
362

    
363
    public static final int TOUCHES = 3;
364

    
365
    public static final int CROSSES = 4;
366

    
367
    public static final int WITHIN = 5;
368

    
369
    public static final int CONTAINS = 6;
370

    
371
    public static final int OVERLAPS = 7;
372

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

    
381
    /**
382
     * A layer with graphical items: geometries and symbols.
383
     *
384
     * @see #getGraphicsLayer()
385
     * @see #setGraphicsLayer(GraphicLayer)
386
     * @see #print(Graphics2D, double, PrintAttributes)
387
     */
388
//    private GraphicLayer tracLayer = null;
389
    private static final String DEFAULT_TRACTLAYER = "Default";
390
    private Map<String, VectorLayer> tracLayers;
391
    //MapContextLocator.getMapContextManager().createGraphicsLayer(getProjection());
392

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

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

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

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

    
443
    /**
444
     * Event listener for the collection of layers of this map.
445
     */
446
    private LayerEventListener layerEventListener = null;
447

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

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

    
467
    /**
468
     * Layer order manager decides position of layers added to this map context.
469
     */
470
    private LayerOrderManager orderManager = null;
471

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

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

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

    
503
    private long layersVersion = 0L;
504
    private long viewPortVersion = 0L;
505
    private long graphicsLayerVersion = 0L;
506

    
507
    /**
508
     * Object to Manage Draw of MapContext
509
     */
510
    private MapContextDrawer mapContextDrawer = null;
511

    
512
    /**
513
     * Object to Manage Draw of MapContext
514
     */
515
    private Class mapContextDrawerClass = null;
516

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

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

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

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

    
556
    public MapContext() {
557
        this.tracLayers = new LinkedHashMap<>();
558
        layerEventListener = new LayerEventListener();
559
    }
560

    
561
    /**
562
     * <p>
563
     * Creates a new map context with the layers and the drawing information
564
     * defined in the view port arguments.</p>
565
     *
566
     * @param fLayers the initial hierarchy of nodes of layers that this map
567
     * will have
568
     * @param vp information for drawing the layers of this map in the available
569
     * rectangular area according a projection
570
     */
571
    public MapContext(FLayers fLayers, ViewPort vp) {
572
        this();
573
        this.layers = fLayers;
574
        if (layers != null) {
575
            layers.setMapContext(this);
576
            layers.addLayerCollectionListener(layerEventListener);
577
            layers.addLayerCollectionListener(eventBuffer);
578
        }
579
        setViewPort(vp);
580
    }
581

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
831
        CompatLocator.getGraphicsUtils().setRenderingHintsForPrinting(g);
832

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

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

    
841
            }
842
        };
843
        this.getMapContextDrawer().print(this.layers, g, cancel, scale, properties);
844
        for (VectorLayer tracLayer : this.tracLayers.values()) {
845
            if (tracLayer != null) {
846
                tracLayer.draw(null, g, viewPort, cancel, scale);
847
            }
848
        }
849
    }
850

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

    
866
        return ret;
867
    }
868

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

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

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

    
920
    }
921

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

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

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

    
968
        if (viewPort.getImageSize() == null) {
969
            return -1;
970
        }
971

    
972
        if (viewPort.getAdjustedEnvelope() == null) {
973
            return 0;
974
        }
975
        double[] trans2Meter = getDistanceTrans2Meter();
976
        int mUnits = getViewPort().getMapUnits();
977

    
978
        if (proj == null) {
979
            double w = ((viewPort.getImageSize().width / dpi) * 0.0254);
980
            return (long) (viewPort.getAdjustedEnvelope().getLength(0)
981
                    / w * trans2Meter[mUnits]);
982
        }
983

    
984
        return Math.round(proj.getScale(
985
                viewPort.getAdjustedEnvelope().getMinimum(0) * trans2Meter[mUnits],
986
                viewPort.getAdjustedEnvelope().getMaximum(0) * trans2Meter[mUnits],
987
                viewPort.getImageSize().width,
988
                dpi));
989

    
990
    }
991

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

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

    
1038
    /**
1039
     * @see
1040
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
1041
     */
1042
    public void process(Visitor visitor) {
1043
    }
1044

    
1045
    /**
1046
     * @see
1047
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
1048
     */
1049
    public void processSelected(Visitor visitor) {
1050
    }
1051

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

    
1060
    /**
1061
     * @see
1062
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
1063
     */
1064
    public void selectFromSelection() {
1065
    }
1066

    
1067
    /**
1068
     * @see
1069
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
1070
     */
1071
    public void createIndex() {
1072
    }
1073

    
1074
    /**
1075
     * @see org.cresques.geo.Projected#getProjection()
1076
     *
1077
     * @see ViewPort#getProjection()
1078
     * @see #setProjection(IProjection)
1079
     * @see #reProject(ICoordTrans)
1080
     */
1081
    public IProjection getProjection() {
1082
        return getViewPort().getProjection();
1083
    }
1084

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

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

    
1108
    public Envelope getSelectionBounds() throws BaseException {
1109

    
1110
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1111

    
1112
        layers.accept(visitor);
1113
        Envelope env_in_data_crs = visitor.getSelectioEnvelope();
1114
        return env_in_data_crs;
1115
    }
1116

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

    
1152
        CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1153

    
1154
        this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1155
    }
1156

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

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

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

    
1217
            public void setCanceled(boolean canceled) {
1218
                // Do nothing
1219
            }
1220
        }, scale);
1221
    }
1222

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

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

    
1252
        if (this.mapContextDrawer != null) {
1253
            this.mapContextDrawer.setViewPort(viewPort);
1254
        }
1255

    
1256
        this.viewPort = viewPort;
1257
        if (viewPort != null) {
1258
            viewPort.addViewPortListener(eventBuffer);
1259
        }
1260
    }
1261

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

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

    
1287
        for (VectorLayer tracLayer : this.tracLayers.values()) {
1288
            if (tracLayer != null) {
1289
                Envelope graphicsEnvelope = tracLayer.getFullEnvelope();
1290
                if( graphicsEnvelope==null ) {
1291
                    continue;
1292
                }
1293
                if (envelope == null) {
1294
                    try {
1295
                        envelope =  (Envelope) graphicsEnvelope.clone();
1296
                    } catch (CloneNotSupportedException ex) {
1297
                    }
1298
                } else if (graphicsEnvelope != null) {
1299
                    envelope.add(graphicsEnvelope);
1300
                }
1301
            }
1302
        }
1303
        return envelope;
1304
    }
1305

    
1306
    /**
1307
     * <p>
1308
     * Adds a listener of atomic events to the internal
1309
     * {@link EventBuffer EventBuffer}.</p>
1310
     *
1311
     * @param listener the new listener
1312
     *
1313
     * @return <code>true</code> if has added the listener successfully
1314
     *
1315
     * @see #removeAtomicEventListener(AtomicEventListener)
1316
     * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1317
     */
1318
    public boolean addAtomicEventListener(AtomicEventListener listener) {
1319
        return eventBuffer.addAtomicEventListener(listener);
1320
    }
1321

    
1322
    /**
1323
     * <p>
1324
     * Removes a listener of atomic events from the internal
1325
     * {@link EventBuffer EventBuffer}.</p>
1326
     *
1327
     * @param listener the listener to remove
1328
     *
1329
     * @return <tt>true</tt> if the list contained the specified element
1330
     *
1331
     * @see #addAtomicEventListener(AtomicEventListener)
1332
     * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1333
     */
1334
    public boolean removeAtomicEventListener(AtomicEventListener listener) {
1335
        return eventBuffer.removeAtomicEventListener(listener);
1336
    }
1337

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

    
1347
    /**
1348
     * @see EventBuffer#endAtomicEvent()
1349
     *
1350
     * @see #beginAtomicEvent()
1351
     */
1352
    public void endAtomicEvent() {
1353
        eventBuffer.endAtomicEvent();
1354
    }
1355

    
1356
    /**
1357
     * <p>
1358
     * The class <code>LayerEventListener</code> implements the methods of
1359
     * {@link LayerCollectionListener LayerCollectionListener} that handles the
1360
     * "layer added" or "layer removed" events in a map.</p>
1361
     * <p>
1362
     * Is designed as a listener for all layers in a
1363
     * {@link MapContext MapContext}.</p>
1364
     *
1365
     * @author Fernando Gonz?lez Cort?s
1366
     */
1367
    public class LayerEventListener extends BaseLayerCollectionListener {
1368

    
1369
        @Override
1370
        public void layerAdded(LayerCollectionEvent e) {
1371
            // Voy a mover todo esto a la vista
1372
//            // Si aun no tenemos envelope, problablemente sera la primera capa,
1373
//            // asi que asignamos el envelope de la capa al mapcontext.
1374
//            if (getViewPort().getEnvelope() == null) {
1375
//                FLayer lyr = e.getAffectedLayer();
1376
//                if (lyr.isAvailable()) {
1377
//                    try {
1378
//                        getViewPort().setEnvelope(lyr.getFullEnvelope());
1379
//                    } catch (Exception ex) {
1380
//                        logger.warn(
1381
//                                MessageFormat.format(
1382
//                                    "Can''t set envelope to view port from layer ''{0}''",
1383
//                                    new Object[]{lyr.getName()}
1384
//                                ),
1385
//                                ex
1386
//                        );
1387
//                    }
1388
//                }
1389
//            }
1390

    
1391
            // Registramos al FMap como listener del legend de las capas
1392
            FLayer lyr = e.getAffectedLayer();
1393
            addSelectionListener(lyr);
1394
        }
1395

    
1396
        @Override
1397
        public void layerRemoved(LayerCollectionEvent e) {
1398
            FLayer lyr = e.getAffectedLayer();
1399

    
1400
            lyr.removeLayerListener(eventBuffer);
1401

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

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

    
1412
        }
1413

    
1414
    }
1415

    
1416
    /**
1417
     * <p>
1418
     * Adds the {@link LayerEventListener LayerEventListener} of this map to the
1419
     * collection of layers argument.</p>
1420
     *
1421
     * @param a collection of layers
1422
     */
1423
    public void addAsCollectionListener(FLayers layers2) {
1424
        layers2.addLayerCollectionListener(layerEventListener);
1425
    }
1426

    
1427
    public VectorLayer getGraphicsLayer(String name) {
1428
        return this.tracLayers.get(name);
1429
    }
1430
    
1431
    public void setGraphicsLayer(String name, FLyrVect layer) {
1432
        this.tracLayers.put(name, layer);
1433
    }
1434
    
1435
    public void removeGraphicsLayer(String name) {
1436
        this.tracLayers.remove(name);
1437
    }
1438
    
1439
    /**
1440
     * <p>
1441
     * Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1442
     *
1443
     * @return the graphic layer of this map
1444
     *
1445
     * @see #setGraphicsLayer(GraphicLayer)
1446
     */
1447
    public GraphicLayer getGraphicsLayer() {
1448
        GraphicLayer tracLayer = (GraphicLayer) this.tracLayers.get(DEFAULT_TRACTLAYER);
1449
        if (tracLayer == null) {
1450
            if (getViewPort() != null) {
1451
                tracLayer
1452
                        = MapContextLocator.getMapContextManager()
1453
                        .createGraphicsLayer(
1454
                                getViewPort().getProjection());
1455
            } else {
1456
                tracLayer
1457
                        = MapContextLocator.getMapContextManager()
1458
                        .createGraphicsLayer(null);
1459
            }
1460
            this.tracLayers.put(DEFAULT_TRACTLAYER, tracLayer);
1461
        }
1462
        return tracLayer;
1463
    }
1464

    
1465
    /**
1466
     * <p>
1467
     * Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1468
     *
1469
     * @param graphicLayer the new graphic layer
1470
     *
1471
     * @see #getGraphicsLayer()
1472
     */
1473
    public void setGraphicsLayer(GraphicLayer graphicLayer) {
1474
        this.tracLayers.put(DEFAULT_TRACTLAYER, graphicLayer);
1475
    }
1476

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

    
1511
                if (!getLayers().getLayer(i).getName().equals(
1512
                        map.getLayers().getLayer(i).getName())) {
1513
                    isEqual = false;
1514
                }
1515

    
1516
            }
1517
        } else {
1518
            isEqual = false;
1519
        }
1520
        return isEqual;
1521
    }
1522

    
1523
    /**
1524
     * <p>
1525
     * Registers the message of an error associated to this map.</p>
1526
     *
1527
     * @param stringProperty the error message
1528
     *
1529
     * @see #getLayersError()
1530
     * @see #clearErrors()
1531
     */
1532
    public void addLayerError(String stringProperty) {
1533
        layersError.add(stringProperty);
1534
    }
1535

    
1536
    /**
1537
     * <p>
1538
     * Gets the list with all error messages registered to this map.</p>
1539
     *
1540
     * @return the list of errors registered to this map
1541
     *
1542
     * @see #addLayerError(String)
1543
     * @see #clearErrors()
1544
     */
1545
    public ArrayList getLayersError() {
1546
        return layersError;
1547
    }
1548

    
1549
    /**
1550
     * <p>
1551
     * Removes all error messages associated to this map.</p>
1552
     *
1553
     * @see #addLayerError(String)
1554
     * @see #getLayersError()
1555
     */
1556
    public void clearErrors() {
1557
        layersError.clear();
1558
    }
1559

    
1560
    /**
1561
     * <p>
1562
     * Creates and returns a new group of layers that belongs to this
1563
     * <code>MapContext</code>.</p>
1564
     *
1565
     * @param parent layer node in this <code>MapContexte</code> that will be
1566
     * the parent of the new node
1567
     * @return the new layer node
1568
     */
1569
    public FLayers getNewGroupLayer(FLayers parent) {
1570
        FLayers group1 = new FLayers();//(this,parent);
1571
        group1.setMapContext(this);
1572
        group1.setParentLayer(parent);
1573
        return group1;
1574
    }
1575

    
1576
    public String getClassName() {
1577
        return null;
1578
    }
1579

    
1580
    public ArrayList getLayersToSnap() {
1581
        return layersToSnap;
1582
    }
1583

    
1584
    public void setLayersToSnap(ArrayList layersToSnap) {
1585
        this.layersToSnap = layersToSnap;
1586

    
1587
    }
1588

    
1589
    public void update(Observable observable, Object notification) {
1590
        // TODO REVISAR ESTO!!!
1591
        String ntype = null;
1592
        if (notification instanceof FeatureStoreNotification) {
1593
            FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1594
            ntype = fsNotification.getType();
1595
            if (ntype.equals(FeatureStoreNotification.LOAD_FINISHED)
1596
                    || ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)) {
1597
                getLayers().moveTo(0, 0);
1598
            }
1599
        }
1600
    }
1601

    
1602
    public long getDrawVersion() {
1603
        if (getViewPort().getDrawVersion() > this.viewPortVersion
1604
                || getLayers().getDrawVersion() > this.layersVersion
1605
                || getGraphicsLayer().getDrawVersion() > graphicsLayerVersion) {
1606
            updateDrawVersion();
1607
        }
1608
        return this.drawVersion;
1609
    }
1610

    
1611
    protected void updateDrawVersion() {
1612
        this.layersVersion = getLayers().getDrawVersion();
1613
        this.viewPortVersion = getViewPort().getDrawVersion();
1614
        this.graphicsLayerVersion = getGraphicsLayer().getDrawVersion();
1615
        this.drawVersion++;
1616
    }
1617

    
1618
    public MapContextDrawer getMapContextDrawer() throws ReadException,
1619
            MapContextException {
1620
        if (this.mapContextDrawer == null) {
1621
            if (mapContextDrawerClass == null) {
1622
                this.mapContextDrawer = mapContextManager
1623
                        .createDefaultMapContextDrawerInstance();
1624
            } else {
1625
                this.mapContextDrawer = mapContextManager
1626
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1627
            }
1628
            this.mapContextDrawer.setMapContext(this);
1629
            this.mapContextDrawer.setViewPort(viewPort);
1630
        }
1631

    
1632
        return this.mapContextDrawer;
1633
    }
1634

    
1635
    public void setMapContextDrawerClass(Class mapContextDrawerClass)
1636
            throws MapContextException {
1637
        mapContextManager.validateMapContextDrawer(mapContextDrawerClass);
1638
        this.mapContextDrawerClass = mapContextDrawerClass;
1639
        if (this.mapContextDrawer != null) {
1640
            this.mapContextDrawer.dispose();
1641
            this.mapContextDrawer = null;
1642
        }
1643
    }
1644

    
1645
    public void setMapContextDrawer(MapContextDrawer drawer) {
1646
        if (this.mapContextDrawer != null) {
1647
            this.mapContextDrawer.dispose();
1648
            this.mapContextDrawer = null;
1649
        }
1650
        this.mapContextDrawer = drawer;
1651
        if (this.mapContextDrawer != null) {
1652
            this.mapContextDrawer.setMapContext(this);
1653
            this.mapContextDrawer.setViewPort(viewPort);
1654
        }
1655
    }
1656

    
1657
    public void loadFromState(PersistentState state)
1658
            throws PersistenceException {
1659

    
1660
        ViewPort vp = (ViewPort) state.get("ViewPort");
1661
        setViewPort(vp);
1662

    
1663
        layers = (FLayers) state.get("layers");
1664
        layers.setName("root layer");
1665
        loadLayers(layers);
1666
        layers.setMapContext(this);
1667

    
1668
        layerEventListener = new LayerEventListener();
1669
        layers.addLayerCollectionListener(layerEventListener);
1670

    
1671
        layers.addLayerCollectionListener(eventBuffer);
1672
        layers.setProjection(vp.getProjection());
1673

    
1674
        //Add the listener for the selection
1675
        addSelectionListener(layers);
1676

    
1677
        // ======================
1678
        if (state.hasValue("orderManager")) {
1679
            LayerOrderManager lom = (LayerOrderManager) state.get("orderManager");
1680
            this.setOrderManager(lom);
1681
        }
1682
        DefaultMapContextManager manager = (DefaultMapContextManager) MapContextLocator.getMapContextManager();
1683
        manager.notifyLoadMapContext(this);
1684
    }
1685

    
1686
    private void loadLayers(FLayers lyrs) {
1687

    
1688
        int sz = lyrs.getLayersCount();
1689
        for (int i = 0; i < sz; i++) {
1690
            try {
1691
                lyrs.getLayer(i).load();
1692
            } catch (LoadLayerException e) {
1693
                logger.error("While loading layer: " + lyrs.getLayer(i).getName());
1694
            }
1695
        }
1696
    }
1697

    
1698
    public void saveToState(PersistentState state) throws PersistenceException {
1699
        state.set("ViewPort", viewPort);
1700
        state.set("layers", (Persistent)layers);
1701
        state.set("orderManager", this.getOrderManager());
1702
    }
1703

    
1704
    public static class RegisterPersistence implements Callable {
1705

    
1706
        public Object call() {
1707
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1708
            DynStruct definition = manager.addDefinition(
1709
                    MapContext.class,
1710
                    "MapContext",
1711
                    "MapContext Persistence definition",
1712
                    null,
1713
                    null
1714
            );
1715
            definition.addDynFieldObject("ViewPort")
1716
                    .setClassOfValue(ViewPort.class)
1717
                    .setMandatory(true);
1718

    
1719
            definition.addDynFieldObject("layers")
1720
                    .setClassOfValue(FLayers.class)
1721
                    .setMandatory(true);
1722

    
1723
            definition.addDynFieldObject("orderManager")
1724
                    .setClassOfValue(LayerOrderManager.class)
1725
                    .setMandatory(false);
1726

    
1727
            return Boolean.TRUE;
1728
        }
1729
    }
1730

    
1731
    protected void doDispose() throws BaseException {
1732
        dispose(layers);
1733
        for (VectorLayer tracLayer : this.tracLayers.values()) {
1734
            dispose(tracLayer);
1735
        }
1736
    }
1737

    
1738
    /**
1739
     * <p>
1740
     * Registers an event buffer as a listener for all layers as argument.</p>
1741
     *
1742
     * <p>
1743
     * Each {@link FLayer FLayer} of this map must have an event buffer for all
1744
     * kind of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1745
     * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers}
1746
     * layers, and for each one, registers, for their specific listeners, the
1747
     * <code>eventBuffer</code> as a listener.</p>
1748
     *
1749
     * @param the layer or layers
1750
     */
1751
    private void addSelectionListener(FLayer lyr) {
1752
        lyr.addLayerListener(eventBuffer);
1753

    
1754
        if (lyr instanceof Classifiable) {
1755
            Classifiable c = (Classifiable) lyr;
1756
            c.addLegendListener(eventBuffer);
1757
        }
1758

    
1759
        if (lyr instanceof FLayers) {
1760
            FLayers lyrs = (FLayers) lyr;
1761
            for (int i = 0; i < lyrs.getLayersCount(); i++) {
1762
                addSelectionListener(lyrs.getLayer(i));
1763
            }
1764
        }
1765
        if (lyr instanceof SingleLayer) {
1766
            if (((SingleLayer) lyr).getDataStore() != null) {
1767
                ((SingleLayer) lyr).getDataStore().addObserver(
1768
                        MapContext.this);
1769
            }
1770
        }
1771
    }
1772

    
1773
    public void setOrderManager(LayerOrderManager lom) {
1774
        orderManager = lom;
1775
    }
1776

    
1777
    public LayerOrderManager getOrderManager() {
1778

    
1779
        if (orderManager == null) {
1780
            orderManager = MapContextLocator.getDefaultOrderManager();
1781
        }
1782
        return orderManager;
1783
    }
1784

    
1785
    public boolean hasVectorLayers() {
1786
        return this.hasVectorLayers(this.getLayers());
1787
    }
1788

    
1789
    public boolean hasActiveVectorLayers() {
1790
        FLayer[] layers = this.getLayers().getActives();
1791
        for (int i = 0; i < layers.length; i++) {
1792
            FLayer layer = layers[i];
1793
            if (layer.isAvailable() && layer instanceof FLyrVect) {
1794
                return true;
1795
            }
1796
        }
1797
        return false;
1798
    }
1799

    
1800
    public boolean hasActiveLayers() {
1801
        FLayer[] layers = this.getLayers().getActives();
1802
        return !ArrayUtils.isEmpty(layers);
1803
    }
1804

    
1805
    public boolean hasLayers() {
1806
        return !this.getLayers().isEmpty();
1807
    }
1808
    
1809
    private boolean hasVectorLayers(FLayers layers) {
1810
        for (int i = 0; i < layers.getLayersCount(); i++) {
1811
            FLayer lyr = layers.getLayer(i);
1812
            if (lyr instanceof FLayers) {
1813
                if (hasVectorLayers((FLayers) lyr)) {
1814
                    return true;
1815
                }
1816
            } else if (lyr instanceof FLyrVect) {
1817
                return true;
1818
            }
1819
        }
1820
        return false;
1821
    }
1822
    
1823
    @Override
1824
    public Iterator<FLayer> iterator() {
1825
        return this.layers.iterator();
1826
    }
1827

    
1828
    public Iterator deepiterator() {
1829
        return this.layers.deepiterator();
1830
    }
1831

    
1832
    public MapTimeContext getTimeContext() {
1833
        TimeSupportManager timeSupportManager = TimeSupportLocator.getManager();
1834
        Interval interval = null;
1835
        final List<Instant> times = new ArrayList<>();
1836
        Instant minInstant = null;
1837
        Instant maxInstant = null;
1838

    
1839
        //TODO Separate absolute and relative time
1840
        FLayers layers = this.getLayers();
1841
        for( Iterator<FLayer> iterator = layers.deepiterator(); iterator.hasNext(); ) {
1842
            FLayer layer = iterator.next();
1843
            if( layer instanceof SingleLayer ) {
1844
                DataStore dataStore = ((SingleLayer) layer).getDataStore();
1845
                if( dataStore.getInterval() != null ) {
1846
                    Instant startInstant = dataStore.getInterval().getStart();
1847
                    Instant endInstant = dataStore.getInterval().getEnd();
1848
                    times.addAll(dataStore.getTimes());
1849
                    if( minInstant == null ) {
1850
                        minInstant = startInstant;
1851
                        maxInstant = endInstant;
1852
                    } else {
1853
                        if( minInstant.isAfter(startInstant) ) {
1854
                            minInstant = startInstant;
1855
                        }
1856
                        if( maxInstant.isBefore(endInstant) ) {
1857
                            maxInstant = endInstant;
1858
                        }
1859
                    }
1860
                }
1861
            }
1862
        }
1863

    
1864
        if( minInstant != null ) {
1865
            if( minInstant.isAbsolute() ) {
1866
                try {
1867
                    interval = timeSupportManager.createAbsoluteInterval((AbsoluteInstant) minInstant, (AbsoluteInstant) maxInstant);
1868
                } catch (AbsoluteIntervalTypeNotRegisteredException e) {
1869
                    logger.warn("Error creating the time interval", e);
1870
                }
1871
            } else {
1872
                interval = timeSupportManager.createRelativeInterval(((RelativeInstant) minInstant).toMillis(), ((RelativeInstant) maxInstant).toMillis());
1873
            }
1874
        }
1875
        final Interval tmp_interval = interval;
1876
        return new MapTimeContext() {
1877
            @Override
1878
            public Interval getInterval() {
1879
                return tmp_interval;
1880
            }
1881

    
1882
            @Override
1883
            public List<Instant> getTimes() {
1884
                return times;
1885
            }
1886
        };
1887
    }
1888

    
1889
}