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

History | View | Annotate | Download (63.8 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or modify it under
7
 * the terms of the GNU General Public License as published by the Free Software
8
 * Foundation; either version 3 of the License, or (at your option) any later
9
 * version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but WITHOUT
12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13
 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14
 * details.
15
 *
16
 * You should have received a copy of the GNU General Public License along with
17
 * this program; if not, write to the Free Software Foundation, Inc., 51
18
 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19
 *
20
 * For any additional information, do not hesitate to contact us at info AT
21
 * gvsig.com, or visit our website www.gvsig.com.
22
 */
23
package org.gvsig.fmap.mapcontext;
24

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

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

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

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

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

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

    
134
    public static ArrayList AREANAMES = new ArrayList();
135
    public static ArrayList AREAABBR = new ArrayList();
136
    public static ArrayList AREATRANS2METER = new ArrayList();
137

    
138
    public static ArrayList DISTANCENAMES = new ArrayList();
139
    public static ArrayList DISTANCEABBR = new ArrayList();
140
    public static ArrayList DISTANCETRANS2METER = new ArrayList();
141

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

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

    
163
    }
164

    
165
    private static final GeometryManager GEOM_MANAGER = GeometryLocator.getGeometryManager();
166

    
167
    private static final MapContextManager MAPCONTEXT_MANAGER = MapContextLocator.getMapContextManager();
168

    
169
    private static final Logger LOGGER = LoggerFactory.getLogger(MapContext.class);
170

    
171
    private long scaleToUseWhenEnvelopeCollapse = 10000000;
172

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
474
    private long layersVersion = 0L;
475
    private long viewPortVersion = 0L;
476
    private long graphicsLayerVersion = 0L;
477

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

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

    
488
    /**
489
     * <p>
490
     * Color used to represent the selections.</p>
491
     */
492
    private static Color selectionColor = Color.YELLOW;
493
    private ArrayList layersToSnap = new ArrayList();
494

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
788
        CompatLocator.getGraphicsUtils().setRenderingHintsForPrinting(g);
789

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

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

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

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

    
823
        return ret;
824
    }
825

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

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

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

    
877
    }
878

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

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

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

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

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

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

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

    
947
    }
948

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

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

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

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

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

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

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

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

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

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

    
1065
    public Envelope getSelectionBounds() throws BaseException {
1066

    
1067
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1068

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1377
            lyr.removeLayerListener(eventBuffer);
1378

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

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

    
1389
        }
1390

    
1391
    }
1392

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

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

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

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

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

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

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

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

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

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

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

    
1562
    public ArrayList getLayersToSnap() {
1563
        return layersToSnap;
1564
    }
1565

    
1566
    public void setLayersToSnap(ArrayList layersToSnap) {
1567
        this.layersToSnap = layersToSnap;
1568

    
1569
    }
1570

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

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

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

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

    
1614
        return this.mapContextDrawer;
1615
    }
1616

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

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

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

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

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

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

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

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

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

    
1668
    private void loadLayers(FLayers lyrs) {
1669

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

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

    
1686
    public static class RegisterPersistence implements Callable {
1687

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

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

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

    
1709
            return Boolean.TRUE;
1710
        }
1711
    }
1712

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

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

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

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

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

    
1759
    public LayerOrderManager getOrderManager() {
1760

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

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

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

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

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

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

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

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

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

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

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

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

    
1879
}