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

History | View | Annotate | Download (62.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.text.MessageFormat;
31
import java.util.ArrayList;
32
import java.util.List;
33

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

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

    
84
/**
85
 * <p>
86
 * The <code>MapContext</code> class represents the model and a part of the
87
 * control and view around graphical layers used by
88
 * {@link MapControl MapControl}.</p>
89
 *
90
 * <p>
91
 * An instance of <code>MapContext</code> is made up with:
92
 * <ul>
93
 * <li>a hierarchy of {@link FLayers FLayers} nodes
94
 * <li>a {@link GraphicLayer GraphicLayer} layer
95
 * <li>a {@link ViewPort ViewPort}
96
 * <li>an {@link EventBuffer EventButter}
97
 * <li>some
98
 * {@link com.iver.cit.gvsig.fmap.layers.LegendListener LegendListener}s
99
 * <li>some {@link LayerDrawingListener LayerDrawingListener}s
100
 * <li>some {@link ErrorListener ErrorListener}s
101
 * </ul>
102
 * </p>
103
 *
104
 * @author Fernando Gonz?lez Cort?s
105
 */
106
public class MapContext extends AbstractDisposable implements Projected,
107
        Persistent, Observer {
108

    
109
    /**
110
     * <p>
111
     * Defines the value which a unit of a distance measurement must be divided
112
     * to obtain its equivalent <b>in meters</b>.</p>
113
     *
114
     * <p>
115
     * <b><i>Conversion values of distance measurements:</i></b>
116
     * <ul>
117
     * <li><code>MapContext.CHANGEM[0]</code>: kilometer
118
     * <li><code>MapContext.CHANGEM[1]</code>: meter
119
     * <li><code>MapContext.CHANGEM[2]</code>: centimeter
120
     * <li><code>MapContext.CHANGEM[3]</code>: millimeter
121
     * <li><code>MapContext.CHANGEM[4]</code>: international statute mile
122
     * <li><code>MapContext.CHANGEM[5]</code>: yard
123
     * <li><code>MapContext.CHANGEM[6]</code>: foot
124
     * <li><code>MapContext.CHANGEM[7]</code>: inch
125
     * <li><code>MapContext.CHANGEM[8]</code>: grade
126
     * </ul>
127
     *
128
     * <p>
129
     * <h3>Examples:</h3>
130
     * <pre>1 international statute mile / MapContext.CHANGEM[4] = X meters</pre>
131
     * <pre>1 kilometer / MapContext.CHANGEM[0] = X meters</pre>
132
     * <pre>1 grade / MapContext.CHANGEM[8] = X meters</pre>
133
     * </p>
134
     *
135
     * <p>
136
     * <h3>Grade conversion value: <code>MapContext.CHANGEM[8]</code></h3>
137
     * The value of <code>MapContext.CHANGEM[8]</code> represents the meters of
138
     * a straight line between two points on the Earth surface that are 1 grade
139
     * far each other of the center of the Earth. This value has been calculated
140
     * using a radius approximated of R<sub>Earth</sub>=6.37846082678100774672e6
141
     * meters, according these equations:
142
     * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
143
     * <pre>MapContext.CHANGEM[8] = 1 / D</pre>
144
     * <h4>Explanation:</h4>
145
     * We get an isosceles triangle with the center of the Earth and the 2
146
     * points on the surface. This triangle can be divided into two rectangle
147
     * triangles. We know two values, the angle of 1 grade, that will be 0.50
148
     * grades in each triangle, and the Earth radius that is the hypotenuse.
149
     * Then we apply trigonometry and get the distance <i>D</i> between both
150
     * points on the Earth surface.</p>
151
     * <p>
152
     * Now we only must invert that value to obtain
153
     * <code>MapContext.CHANGEM[8]</code>.</p>
154
     *
155
     * @deprecated use getDistanceTrans2Meter()
156
     */
157
    public static final double[] CHANGEM = {1000, 1, 0.01, 0.001, 1609.344,
158
        0.9144, 0.3048, 0.0254, 1 / 8.983152841195214E-6};
159

    
160
    public static ArrayList AREANAMES = new ArrayList();
161
    public static ArrayList AREAABBR = new ArrayList();
162
    public static ArrayList AREATRANS2METER = new ArrayList();
163

    
164
    public static ArrayList DISTANCENAMES = new ArrayList();
165
    public static ArrayList DISTANCEABBR = new ArrayList();
166
    public static ArrayList DISTANCETRANS2METER = new ArrayList();
167

    
168
    static {
169
        MapContext.addDistanceUnit("Kilometros", "Km", 1000);
170
        MapContext.addDistanceUnit("Metros", "m", 1);
171
        MapContext.addDistanceUnit("Centimetros", "cm", 0.01);
172
        MapContext.addDistanceUnit("Milimetros", "mm", 0.001);
173
        MapContext.addDistanceUnit("Millas", "mi", 1609.344);
174
        MapContext.addDistanceUnit("Yardas", "Ya", 0.9144);
175
        MapContext.addDistanceUnit("Pies", "ft", 0.3048);
176
        MapContext.addDistanceUnit("Pulgadas", "inche", 0.0254);
177
        MapContext.addDistanceUnit("Grados", "?", 1 / 8.983152841195214E-6);
178

    
179
        MapContext.addAreaUnit("Kilometros", "Km", true, 1000);
180
        MapContext.addAreaUnit("Metros", "m", true, 1);
181
        MapContext.addAreaUnit("Centimetros", "cm", true, 0.01);
182
        MapContext.addAreaUnit("Milimetros", "mm", true, 0.001);
183
        MapContext.addAreaUnit("Millas", "mi", true, 1609.344);
184
        MapContext.addAreaUnit("Yardas", "Ya", true, 0.9144);
185
        MapContext.addAreaUnit("Pies", "ft", true, 0.3048);
186
        MapContext.addAreaUnit("Pulgadas", "inche", true, 0.0254);
187
        MapContext.addAreaUnit("Grados", "?", true, 1 / 8.983152841195214E-6);
188

    
189
    }
190

    
191
    private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
192

    
193
    private static final MapContextManager mapContextManager = MapContextLocator
194
            .getMapContextManager();
195

    
196
    private static final Logger logger = LoggerFactory.getLogger(MapContext.class);
197

    
198
    /**
199
     * <p>
200
     * Determines the number of frames.</p>
201
     *
202
     * <p>
203
     * Number of updates per second that the timer will invoke repaint this
204
     * component.</p>
205
     */
206
    private static int drawFrameRate = 3;
207

    
208
    /**
209
     * <p>
210
     * Returns the draw frame rate.</p>
211
     *
212
     * <p>
213
     * Draw frame rate is the number of repaints of this <code>MapControl</code>
214
     * instance that timer invokes per second.</p>
215
     *
216
     * @return number of repaints of this <code>MapControl</code> instance that
217
     * timer invokes per second
218
     *
219
     * @see #applyFrameRate()
220
     * @see #setDrawFrameRate(int)
221
     */
222
    public static int getDrawFrameRate() {
223
        return drawFrameRate;
224
    }
225

    
226
    /**
227
     * <p>
228
     * Sets the draw frame rate.</p>
229
     *
230
     * <p>
231
     * Draw frame rate is the number of repaints of this <code>MapControl</code>
232
     * instance that timer invokes per second.</p>
233
     *
234
     * @param drawFrameRate number of repaints of this <code>MapControl</code>
235
     * instance that timer invokes per second
236
     *
237
     * @see #applyFrameRate()
238
     * @see #getDrawFrameRate()
239
     */
240
    public static void setDrawFrameRate(int dFR) {
241
        drawFrameRate = dFR;
242
    }
243

    
244
    public static void addAreaUnit(String name, String abbr, boolean isLinear, double trans2meter) {
245
        if (!AREANAMES.contains(name)) {
246
            AREANAMES.add(name);
247
            String pow = "";
248
            if (isLinear) {
249
                pow = String.valueOf((char) 178);
250
            }
251
            AREAABBR.add(abbr + pow);
252
            AREATRANS2METER.add(new Double(trans2meter));
253
        }
254
    }
255

    
256
    public static String[] getAreaNames() {
257
        return (String[]) AREANAMES.toArray(new String[0]);
258
    }
259

    
260
    public static String[] getAreaAbbr() {
261
        return (String[]) AREAABBR.toArray(new String[0]);
262
    }
263

    
264
    public static double[] getAreaTrans2Meter() {
265
        int size = AREATRANS2METER.size();
266
        double[] trans2meters = new double[size];
267
        for (int i = 0; i < size; i++) {
268
            trans2meters[i] = ((Double) AREATRANS2METER.get(i)).doubleValue();
269
        }
270
        return trans2meters;
271
    }
272

    
273
    public static String getOfLinear(int i) {
274
        if (((String) AREAABBR.get(i)).toLowerCase().endsWith(String.valueOf((char) 178))) {
275
            return String.valueOf((char) 178);
276
        }
277
        return "";
278
    }
279

    
280
    public static void addDistanceUnit(String name, String abbr, double trans2meter) {
281
        if (!DISTANCENAMES.contains(name)) {
282
            DISTANCENAMES.add(name);
283
            DISTANCEABBR.add(abbr);
284
            DISTANCETRANS2METER.add(new Double(trans2meter));
285
        }
286
    }
287

    
288
    public static String[] getDistanceNames() {
289
        return (String[]) DISTANCENAMES.toArray(new String[0]);
290
    }
291

    
292
    public String getDistanceName() {
293
        return (String) DISTANCENAMES.get(this.getViewPort().getDistanceUnits());
294
    }
295

    
296
    public static String[] getDistanceAbbr() {
297
        return (String[]) DISTANCEABBR.toArray(new String[0]);
298
    }
299

    
300
    public static double[] getDistanceTrans2Meter() {
301
        int size = DISTANCETRANS2METER.size();
302
        double[] trans2meters = new double[size];
303
        for (int i = 0; i < size; i++) {
304
            trans2meters[i] = ((Double) DISTANCETRANS2METER.get(i)).doubleValue();
305
        }
306
        return trans2meters;
307
    }
308

    
309
    public static int getDistancePosition(String s) {
310
        for (int i = 0; i < DISTANCENAMES.size(); i++) {
311
            if (DISTANCENAMES.get(i).equals(s)) {
312
                return i;
313
            }
314
        }
315
        return 0;
316
    }
317

    
318
    /**
319
     * <p>
320
     * Defines the value which a unit of a distance measurement must be divided
321
     * to obtain its equivalent <b>in centimeters</b>.</p>
322
     *
323
     * <p>
324
     * <b><i>Conversion values of distance measurements:</i></b>
325
     * <ul>
326
     * <li><code>MapContext.CHANGE[0]</code>: kilometer
327
     * <li><code>MapContext.CHANGE[1]</code>: meter
328
     * <li><code>MapContext.CHANGE[2]</code>: centimeter
329
     * <li><code>MapContext.CHANGE[3]</code>: millimeter
330
     * <li><code>MapContext.CHANGE[4]</code>: international statute mile
331
     * <li><code>MapContext.CHANGE[5]</code>: yard
332
     * <li><code>MapContext.CHANGE[6]</code>: foot
333
     * <li><code>MapContext.CHANGE[7]</code>: inch
334
     * <li><code>MapContext.CHANGE[8]</code>: grade
335
     * </ul>
336
     *
337
     * <p>
338
     * <h3>Examples:</h3>
339
     * <pre>1 international statute mile / MapContext.CHANGE[4] = X centimeters</pre>
340
     * <pre>1 kilometer / MapContext.CHANGE[0] = X centimeters</pre>
341
     * <pre>1 grade / MapContext.CHANGE[8] = X centimeters</pre>
342
     * </p>
343
     *
344
     * <p>
345
     * <h3>Grade conversion value: <code>MapContext.CHANGE[8]</code></h3>
346
     * The value of <code>MapContext.CHANGE[8]</code> represents the centimeters
347
     * of a straight line between two points on the Earth surface that are 1
348
     * grade far each other of the center of the Earth. This value has been
349
     * calculated using a radius approximated of
350
     * R<sub>Earth</sub>=6.37846082678100774672e6 meters, according these
351
     * equations:
352
     * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
353
     * <pre>MapContext.CHANGE[8] = 1 / D</pre>
354
     * <h4>Explanation:</h4>
355
     * We get an isosceles triangle with the center of the Earth and the 2
356
     * points on the surface. This triangle can be divided into two rectangle
357
     * triangles. We know two values, the angle of 1 grade, that will be 0.50
358
     * grades in each triangle, and the Earth radius that is the hypotenuse.
359
     * Then we apply trigonometry and get the distance <i>D</i> between both
360
     * points on the Earth surface.</p>
361
     * <p>
362
     * Now we only must invert that value to obtain
363
     * <code>MapContext.CHANGE[8]</code>.</p>
364
     *
365
     * @deprecated use getDistanceTrans2Meter() * 100
366
     */
367
    public static final double[] CHANGE = {100000, 100, 1, 0.1, 160934.4,
368
        91.44, 30.48, 2.54, 1 / 8.983152841195214E-4};
369

    
370
    /* Do not alter the order and the values of this array, if you need append values.*/
371
    /**
372
     * <p>
373
     * Gets the name of all distance measurements supported by
374
     * <code>MapContext</code>.</p>
375
     */
376
//        public static final String[] NAMES= {
377
//                Messages.getString("Kilometros"),
378
//                Messages.getString("Metros"),
379
//                Messages.getString("Centimetros"),
380
//                Messages.getString("Milimetros"),
381
//                Messages.getString("Millas"),
382
//                Messages.getString("Yardas"),
383
//                Messages.getString("Pies"),
384
//                Messages.getString("Pulgadas"),
385
//                Messages.getString("Grados"),
386
//        };
387
    public static final int EQUALS = 0;
388

    
389
    public static final int DISJOINT = 1;
390

    
391
    public static final int INTERSECTS = 2;
392

    
393
    public static final int TOUCHES = 3;
394

    
395
    public static final int CROSSES = 4;
396

    
397
    public static final int WITHIN = 5;
398

    
399
    public static final int CONTAINS = 6;
400

    
401
    public static final int OVERLAPS = 7;
402

    
403
    /**
404
     * A hierarchy of {@link FLayers FLayers} nodes.
405
     *
406
     * @see #getLayers()
407
     * @see #print(Graphics2D, double, PrintAttributes)
408
     */
409
    protected FLayers layers;
410

    
411
    /**
412
     * A layer with graphical items: geometries and symbols.
413
     *
414
     * @see #getGraphicsLayer()
415
     * @see #setGraphicsLayer(GraphicLayer)
416
     * @see #print(Graphics2D, double, PrintAttributes)
417
     */
418
    private GraphicLayer tracLayer = null;
419
    //MapContextLocator.getMapContextManager().createGraphicsLayer(getProjection());
420

    
421
    /**
422
     * Information for draw layers in a view.
423
     *
424
     * @see #getViewPort()
425
     * @see #setViewPort(ViewPort)
426
     */
427
    private ViewPort viewPort;
428

    
429
        // private ArrayList invalidationListeners = new ArrayList();
430
    /**
431
     * Array list with all {@link LegendListener LegendListener} registered to
432
     * this map.
433
     *
434
     * @see #addLayerListener(LegendListener)
435
     * @see #removeLayerListener(LegendListener)
436
     * @see #callLegendChanged()
437
     */
438
    private ArrayList legendListeners = new ArrayList();
439

    
440
    /**
441
     * Array list with all {@link LayerDrawingListener LayerDrawingListener}
442
     * registered to this map.
443
     *
444
     * @see #addLayerDrawingListener(LayerDrawingListener)
445
     * @see #removeLayerDrawListener(LayerDrawingListener)
446
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
447
     */
448
    private ArrayList layerDrawingListeners = new ArrayList();
449

    
450
    /**
451
     * <p>
452
     * Buffer that is used to store and eject events produced on this map:
453
     * <ul>
454
     * <li>Layer collection events.
455
     * <li>View port events.
456
     * <li>Atomic events.
457
     * <li>Layer events.
458
     * <li>Legend events on a {@link Classificable Classificable} layer.
459
     * <li>Selection events on an {@link AlphanumericData AlphanumericData} data
460
     * layer.
461
     * </ul>
462
     * </p>
463
     *
464
     * @see #addAtomicEventListener(AtomicEventListener)
465
     * @see #removeAtomicEventListener(AtomicEventListener)
466
     * @see #beginAtomicEvent()
467
     * @see #endAtomicEvent()
468
     */
469
    private EventBuffer eventBuffer = new EventBuffer();
470

    
471
    /**
472
     * Event listener for the collection of layers of this map.
473
     */
474
    private LayerEventListener layerEventListener = null;
475

    
476
    /**
477
     * List with information of all errors produced on all layers.
478
     *
479
     * @see #addLayerError(String)
480
     * @see #getLayersError()
481
     * @see #clearErrors()
482
     */
483
    private ArrayList layersError = new ArrayList();
484

    
485
    /**
486
     * Array list with all {@link ErrorListener ErrorListener} registered to
487
     * this map.
488
     *
489
     * @see #addErrorListener(ErrorListener)
490
     * @see #removeErrorListener(LegendListener)
491
     * @see #reportDriverExceptions(String, List)
492
     */
493
    private ArrayList errorListeners = new ArrayList();
494

    
495
    /**
496
     * Layer order manager decides position of layers added to this map context.
497
     */
498
    private LayerOrderManager orderManager = null;
499

    
500
        // public static ResourceBundle myResourceBundle =
501
    // ResourceBundle.getBundle("FMap");
502
    /**
503
     * <p>
504
     * Default <i>zoom in</i> factor.</p>
505
     * <p>
506
     * Doing a <i>zoom in</i> operation, decreases the focal distance and
507
     * increases the eyesight angle to the surface. This allows view an smaller
508
     * area but with the items bigger.</p>
509
     */
510
    public static double ZOOMINFACTOR = 2;
511

    
512
    /**
513
     * <p>
514
     * Default <i>zoom out</i> factor.</p>
515
     * <p>
516
     * Doing a <i>zoom out</i> operation, increases the focal distance and
517
     * decreases the eyesight angle to the surface. This allows view a bigger
518
     * area but with the items smaller.</p>
519
     */
520
    public static double ZOOMOUTFACTOR = 0.5;
521

    
522
    /**
523
     *          * Draw version of the context. It's used for know when de componend has
524
     * changed any visualization property
525
     *
526
     * @see getDrawVersion
527
     * @see updateDrawVersion
528
     */
529
    private long drawVersion = 0L;
530

    
531
    private long layersVersion = 0L;
532
    private long viewPortVersion = 0L;
533
    private long graphicsLayerVersion = 0L;
534

    
535
    /**
536
     * Object to Manage Draw of MapContext
537
     */
538
    private MapContextDrawer mapContextDrawer = null;
539

    
540
    /**
541
     * Object to Manage Draw of MapContext
542
     */
543
    private Class mapContextDrawerClass = null;
544

    
545
    /**
546
     * <p>
547
     * Color used to represent the selections.</p>
548
     */
549
    private static Color selectionColor = Color.YELLOW;
550
    private ArrayList layersToSnap = new ArrayList();
551

    
552
    /**
553
     * <p>
554
     * Gets the color used to represent the selections.</p>
555
     *
556
     * @return color used to represent the selections
557
     */
558
    public static Color getSelectionColor() {
559
        return selectionColor;
560
    }
561

    
562
    /**
563
     * <p>
564
     * Sets the color used to represent the selections.</p>
565
     *
566
     * @param selectionColor color used to represent the selections
567
     */
568
    public static void setSelectionColor(Color selectionColor) {
569
        MapContext.selectionColor = selectionColor;
570
    }
571

    
572
    /**
573
     * <p>
574
     * Creates a new map context with the drawing information defined in the
575
     * view port argument, and without layers.</p>
576
     *
577
     * @param vp information for drawing the layers of this map in the available
578
     * rectangular area according a projection
579
     */
580
    public MapContext(ViewPort vp) {
581
        this(new FLayers(), vp);
582
    }
583

    
584
    public MapContext() {
585
    }
586

    
587
    /**
588
     * <p>
589
     * Creates a new map context with the layers and the drawing information
590
     * defined in the view port arguments.</p>
591
     *
592
     * @param fLayers the initial hierarchy of nodes of layers that this map
593
     * will have
594
     * @param vp information for drawing the layers of this map in the available
595
     * rectangular area according a projection
596
     */
597
    public MapContext(FLayers fLayers, ViewPort vp) {
598
        this.layers = fLayers;
599

    
600
        layerEventListener = new LayerEventListener();
601

    
602
        if (layers != null) {
603
            layers.setMapContext(this);
604
            layers.addLayerCollectionListener(layerEventListener);
605
            layers.addLayerCollectionListener(eventBuffer);
606
        }
607

    
608
        setViewPort(vp);
609
    }
610

    
611
    /**
612
     * <p>
613
     * Reports to all driver error listeners registered of a bundle of driver
614
     * exceptions caused in the same map atomic transaction.</p>
615
     *
616
     * @param introductoryText introductory text specified by developer. If
617
     * <code>null</code>, use ""
618
     * @param driverExceptions list with a bundle of driver exceptions caught
619
     * during an atomic event
620
     *
621
     * @see #addErrorListener(ErrorListener)
622
     * @see #removeErrorListener(LegendListener)
623
     */
624
    public synchronized void reportDriverExceptions(String introductoryText,
625
            List driverExceptions) {
626
        for (int i = 0; i < errorListeners.size(); i++) {
627
            ((ErrorListener) errorListeners.get(i)).
628
                    reportDriverExceptions(introductoryText, driverExceptions);
629
        }
630
    }
631

    
632
    /**
633
     * <p>
634
     * Adds the specified legend listener (if didn't exist) to receive legend
635
     * events from this map.</p>
636
     *
637
     * @param listener the legend listener
638
     *
639
     * @see #removeLayerListener(LegendListener)
640
     * @see #callLegendChanged()
641
     */
642
    public void addLayerListener(LegendListener listener) {
643
        if (!legendListeners.contains(listener)) {
644
            legendListeners.add(listener);
645
        }
646
    }
647
        // SUGERENCIA DE PABLO
648
    //        public void addLegendListener(LegendListener listener) {
649
    //                if (!legendListeners.contains(listener))
650
    //                        legendListeners.add(listener);
651
    //        }
652

    
653
    /**
654
     * <p>
655
     * Adds the specified layer drawing listener to catch and handle drawing
656
     * events from layers of this map.</p>
657
     *
658
     * @param listener the listener to add
659
     *
660
     * @see #removeLayerDrawListener(LayerDrawingListener)
661
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
662
     */
663
    public void addLayerDrawingListener(LayerDrawingListener listener) {
664
        layerDrawingListeners.add(listener);
665
    }
666

    
667
    /**
668
     * <p>
669
     * Removes the specified layer drawing listener from this map.</p>
670
     *
671
     * @param listener the listener to remove
672
     *
673
     * @see #addLayerDrawingListener(LayerDrawingListener)
674
     * @see #fireLayerDrawingEvent(LayerDrawEvent)
675
     */
676
    public void removeLayerDrawListener(LayerDrawingListener listener) {
677
        layerDrawingListeners.remove(listener);
678
    }
679

    
680
    /**
681
     * <p>
682
     * Adds the specified error listener to receive error events from this
683
     * map.</p>
684
     *
685
     * @param listener the listener to add
686
     *
687
     * @see #removeErrorListener(LegendListener)
688
     * @see #reportDriverExceptions(String, List)
689
     */
690
    public void addErrorListener(ErrorListener listener) {
691
        errorListeners.add(listener);
692
    }
693

    
694
    /**
695
     * <p>
696
     * Removes the specified error listener from this map.</p>
697
     *
698
     * @param listener the listener to remove
699
     *
700
     * @see #addErrorListener(ErrorListener)
701
     * @see #reportDriverExceptions(String, List)
702
     */
703
    public void removeErrorListener(LegendListener listener) {
704
        legendListeners.remove(listener);
705
    }
706

    
707
        // SUGERENCIA DE PABLO:
708
    //public void removeErrorListener(ErrorListener listener) {
709
    //        errorListeners.remove(listener);
710
    //}
711
    /**
712
     * <p>
713
     * Notifies to all legend listeners registered, that one legend has
714
     * changed.</p>
715
     * <p>
716
     * This method must be called only if it's wanted to reflect a legend
717
     * change.</p>
718
     *
719
     * @see #addLayerListener(LegendListener)
720
     * @see #removeLayerListener(LegendListener)
721
     */
722
    public synchronized void callLegendChanged() {
723
        for (int i = 0; i < legendListeners.size(); i++) {
724
            ((LegendListener) legendListeners.get(i)).legendChanged(null);
725
        }
726
        // getLayers().moveTo(0,0);
727
    }
728

    
729
    /**
730
     * <p>
731
     * Fires a layer drawing event to all
732
     * {@link LayerDrawingListener LayerDrawingListener} listeners registered,
733
     * distinguishing the kind of event.</p>
734
     *
735
     * @param e the event
736
     *
737
     * @see #addLayerDrawingListener(LayerDrawingListener)
738
     * @see #removeLayerDrawListener(LayerDrawingListener)
739
     */
740
    public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
741
        for (int i = 0; i < layerDrawingListeners.size(); i++) {
742
            LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
743
            switch (e.getEventType()) {
744
                case LayerDrawEvent.LAYER_BEFORE_DRAW:
745
                    listener.beforeLayerDraw(e);
746
                    break;
747
                case LayerDrawEvent.LAYER_AFTER_DRAW:
748
                    listener.afterLayerDraw(e);
749
                    break;
750
                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
751
                    listener.beforeGraphicLayerDraw(e);
752
                    break;
753
                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
754
                    listener.afterLayerGraphicDraw(e);
755
                    break;
756
            }
757
        }
758
        // getLayers().moveTo(0,0);
759
    }
760

    
761
    /**
762
     * <p>
763
     * Notifies to all error listeners registered, that one error has been
764
     * produced.</p>
765
     *
766
     * @param e the event with information of the error
767
     *
768
     * @see #addErrorListener(ErrorListener)
769
     * @see #removeErrorListener(LegendListener)
770
     * @see #reportDriverExceptions(String, List)
771
     */
772
    public synchronized void callNewErrorEvent(ErrorEvent e) {
773
        for (int i = 0; i < errorListeners.size(); i++) {
774
            ((ErrorListener) errorListeners.get(i)).errorThrown(e);
775
        }
776
        errorListeners.clear();
777
        // getLayers().moveTo(0,0);
778
    }
779

    
780
    /**
781
     * <p>
782
     * Removes the specified layer listener from this map.</p>
783
     *
784
     * @param listener the listener to remove
785
     *
786
     * @see #addLayerListener(LegendListener)
787
     * @see #callLegendChanged()
788
     */
789
    public void removeLayerListener(LegendListener listener) {
790
        legendListeners.remove(listener);
791
    }
792

    
793
        // SUGERENCIA DE PABLO:
794
    // public void removeLegendListener(LegendListener listener) {
795
    //         legendListeners.remove(listener);
796
    // }
797
    /**
798
     * <p>
799
     * Returns the hierarchy of {@link FLayers FLayers} nodes stored in this
800
     * map.</p>
801
     *
802
     * @return the hierarchy of nodes of layers stored in this map
803
     */
804
    public FLayers getLayers() {
805
        return layers;
806
    }
807

    
808
    /**
809
     * <p>
810
     * Draws the visible layers of this map according its view port, on the
811
     * image parameter.</p>
812
     *
813
     * @param b image with an accessible buffer of image data
814
     */
815
    public void drawLabels(BufferedImage b) {
816
    }
817

    
818
    /**
819
     * @see #redraw()
820
     */
821
    public void invalidate() {
822
        updateDrawVersion();
823
        // Small hack to let the MapControl receive an event and repaint
824
        FLayer layer;
825
        if (layers.getLayersCount() > 0) {
826
            layer = layers.getLayer(layers.getLayersCount() - 1);
827
        } else {
828
            layer = getGraphicsLayer();
829
        }
830
        LayerPositionEvent layerMovedEvent = LayerPositionEvent
831
                .createLayerMovedEvent(
832
                        layer, 0, 0);
833
        eventBuffer.layerMoved(layerMovedEvent);
834
    }
835

    
836
    /**
837
     * <p>
838
     * Prints the layers of this map using the {@link Graphics2D Graphics2D}
839
     * argument, that usually is the {@link Graphics Graphics} of the printer.
840
     * </p>
841
     *
842
     * @param g for rendering 2-dimensional shapes, text and images on the
843
     * Java(tm) platform
844
     * @param scale the scale of the view. Must be between
845
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
846
     * @param properties a set with the settings to be applied to a whole print
847
     * job and to all the documents in the print job
848
     * @throws MapContextException if there is an error getting the instance of
849
     * MapContextDrawer
850
     *
851
     * @throws ReadDriverException if fails reading with driver.
852
     *
853
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
854
     * double)
855
     */
856
    public void print(Graphics2D g, double scale,
857
            PrintAttributes properties) throws ReadException,
858
            MapContextException {
859

    
860
        CompatLocator.getGraphicsUtils().setRenderingHintsForPrinting(g);
861

    
862
        Cancellable cancel = new Cancellable() {
863
            public boolean isCanceled() {
864
                return false;
865
            }
866

    
867
            public void setCanceled(boolean canceled) {
868
                // No queremos que se pueda cancelar la impresi?n.
869

    
870
            }
871
        };
872
        this.getMapContextDrawer().print(this.layers, g, cancel, scale, properties);
873
        if (tracLayer != null) {
874
            tracLayer.draw(null, g, viewPort, cancel, scale);
875
        }
876
    }
877

    
878
    /**
879
     * <p>
880
     * Returns a new <code>MapContext</code> instance with the information of
881
     * the <code>vp</code> argument, and the layers of this map.</p>
882
     *
883
     * @param vp information for drawing the layers of this map in the available
884
     * rectangular area according a projection
885
     *
886
     * @return a new <code>MapContext</code> instance projected by
887
     * <code>vp</code>
888
     */
889
    public MapContext createNewFMap(ViewPort vp) {
890
        MapContext ret = new MapContext(vp);
891
        ret.layers = this.layers;
892

    
893
        return ret;
894
    }
895

    
896
    /**
897
     * <p>
898
     * Creates a new independent <code>MapContext</code> instance, that has a
899
     * clone of the layers and the view port of this one.</p>
900
     * <p>
901
     * The new map will have the same data source drivers to avoid waste memory,
902
     * and work faster.</p>
903
     *
904
     * @return the new <code>MapContext</code> instance
905
     *
906
     * @throws XMLException if fails cloning the view port or a layer
907
     *
908
     * @see FLayer#cloneLayer()
909
     * @see ViewPort#cloneViewPort()
910
     */
911
    public MapContext cloneFMap() {
912
        ViewPort vp;
913
        try {
914
            vp = (ViewPort) getViewPort().clone();
915
        } catch (CloneNotSupportedException e) {
916
            throw new RuntimeException(e);
917
        }
918
        FLayers antLayers = getLayers();
919
        MapContext ret = new MapContext(vp);
920

    
921
        /*
922
         * Cloning order manager
923
         */
924
        LayerOrderManager lom = this.getOrderManager();
925
        try {
926
            lom = (LayerOrderManager) lom.clone();
927
            ret.setOrderManager(lom);
928
        } catch (CloneNotSupportedException e1) {
929
            logger.error("While cloning order manager", e1);
930
        }
931

    
932
        FLayers aux = new FLayers();//(ret,null);
933
        aux.setMapContext(ret);
934
        for (int i = 0; i < antLayers.getLayersCount(); i++) {
935
            FLayer lyr = antLayers.getLayer(i);
936
            try {
937
                FLayer auxLayer = lyr.cloneLayer();
938
                aux.addLayer(auxLayer);
939
                auxLayer.dispose();
940
            } catch (Exception e) {
941
                throw new RuntimeException(e);
942
            }
943
        }
944
        ret.layers = aux;
945
        return ret;
946

    
947
    }
948

    
949
    /**
950
     * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather
951
     * copies them.
952
     *
953
     * @return the new map
954
     */
955
    public MapContext cloneToDraw() {
956
        ViewPort vp;
957
        try {
958
            vp = (ViewPort) getViewPort().clone();
959
            MapContext mapContext = new MapContext(getLayers(), vp);
960
            return mapContext;
961
        } catch (CloneNotSupportedException e) {
962
            throw new RuntimeException(e);
963
        }
964
    }
965

    
966
    /**
967
     * <p>
968
     * Adds a layer to the group of layers that are at a upper level in the
969
     * tree.</p>
970
     *
971
     * @param vectorial the layer to add
972
     */
973
    public void addToTrackLayer(FLayer vectorial) {
974
    }
975

    
976
    /**
977
     * <p>
978
     * Returns the scale of the view in the screen.</p>
979
     *
980
     * @return one of this values:
981
     * <ul>
982
     * <li>the scale of the adjusted extent scale of the view in the screen
983
     * <li><code>-1</code> if there is no image
984
     * <li><code>0</code> if there is no extent defined for the image
985
     * </ul>
986
     *
987
     * @see #setScaleView(long)
988
     * @see ViewPort#getAdjustedExtent()
989
     * @see IProjection#getScale(double, double, double, double)
990
     */
991
    public long getScaleView() {
992
        double dpi = this.getViewPort().getDPI();
993
        IProjection proj = viewPort.getProjection();
994

    
995
        if (viewPort.getImageSize() == null) {
996
            return -1;
997
        }
998

    
999
        if (viewPort.getAdjustedEnvelope() == null) {
1000
            return 0;
1001
        }
1002
        double[] trans2Meter = getDistanceTrans2Meter();
1003
        int mUnits = getViewPort().getMapUnits();
1004

    
1005
        if (proj == null) {
1006
            double w = ((viewPort.getImageSize().width / dpi) * 0.0254);
1007
            return (long) (viewPort.getAdjustedEnvelope().getLength(0)
1008
                    / w * trans2Meter[mUnits]);
1009
        }
1010

    
1011
        return Math.round(proj.getScale(
1012
                viewPort.getAdjustedEnvelope().getMinimum(0) * trans2Meter[mUnits],
1013
                viewPort.getAdjustedEnvelope().getMaximum(0) * trans2Meter[mUnits],
1014
                viewPort.getImageSize().width,
1015
                dpi));
1016

    
1017
    }
1018

    
1019
    /**
1020
     * <p>
1021
     * Sets the new extent of the view, calculated using the scale argument.</p>
1022
     * <p>
1023
     * Doesn't updates the scale if there isn't information about the dimension
1024
     * of the image or the adjusted extent.</p>
1025
     *
1026
     * @param scale the new scale for the view
1027
     *
1028
     * @see ViewPort#setProjection(IProjection)
1029
     * @see #getScaleView()
1030
     */
1031
    public void setScaleView(long scale) {
1032
        double dpi = this.getViewPort().getDPI();
1033
        if (viewPort.getImageSize() == null) {
1034
            return;
1035
        }
1036
        IProjection proj = viewPort.getProjection();
1037
        if (viewPort.getAdjustedExtent() == null) {
1038
            return;
1039
        }
1040
        double[] trans2Meter = getDistanceTrans2Meter();
1041
        Envelope env = viewPort.getAdjustedExtent();
1042
        Rectangle2D r = new Rectangle2D.Double(env.getMinimum(0), env.getMinimum(1), env.getLength(0), env.getLength(1));
1043
        Rectangle2D rec = proj.getExtent(r, scale, viewPort.getImageWidth(), viewPort.getImageHeight(), 100 * getDistanceTrans2Meter()[getViewPort().getMapUnits()], trans2Meter[getViewPort().getDistanceUnits()], dpi);
1044
        try {
1045
            getViewPort().setEnvelope(geomManager.createEnvelope(rec.getX(), rec.getY(), rec.getMaxX(), rec.getMaxY(), SUBTYPES.GEOM2D));
1046
        } catch (CreateEnvelopeException e) {
1047
            logger.error("Error seting the bounding box");
1048
        }
1049
    }
1050

    
1051
    /**
1052
     * <p>
1053
     * Returns the screen resolution (Dots Per Inch) as it was defined by the
1054
     * user's preference, or by default as it is defined in the default
1055
     * Toolkit.</p>
1056
     *
1057
     * Be care, use ViewPort#getDPI to ensure are using the corrects DPI.
1058
     *
1059
     * @return double with the screen's dpi
1060
     */
1061
    public static double getScreenDPI() {
1062
        return CompatLocator.getGraphicsUtils().getScreenDPI();
1063
    }
1064

    
1065
    /**
1066
     * @see
1067
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
1068
     */
1069
    public void process(Visitor visitor) {
1070
    }
1071

    
1072
    /**
1073
     * @see
1074
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
1075
     */
1076
    public void processSelected(Visitor visitor) {
1077
    }
1078

    
1079
    /**
1080
     * @see
1081
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
1082
     * VectorialSubSet)
1083
     */
1084
    public void select(Visitor visitor) {
1085
    }
1086

    
1087
    /**
1088
     * @see
1089
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
1090
     */
1091
    public void selectFromSelection() {
1092
    }
1093

    
1094
    /**
1095
     * @see
1096
     * org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
1097
     */
1098
    public void createIndex() {
1099
    }
1100

    
1101
    /**
1102
     * @see org.cresques.geo.Projected#getProjection()
1103
     *
1104
     * @see ViewPort#getProjection()
1105
     * @see #setProjection(IProjection)
1106
     * @see #reProject(ICoordTrans)
1107
     */
1108
    public IProjection getProjection() {
1109
        return getViewPort().getProjection();
1110
    }
1111

    
1112
    /**
1113
     * <p>
1114
     * Sets the new projection.</p>
1115
     *
1116
     * @param proj the new projection
1117
     *
1118
     * @see #getProjection()
1119
     * @see ViewPort#setProjection(IProjection)
1120
     * @see #reProject(ICoordTrans)
1121
     */
1122
    public void setProjection(IProjection proj) {
1123
        if (getViewPort() != null) {
1124
            getViewPort().setProjection(proj);
1125
        }
1126
    }
1127

    
1128
    /**
1129
     * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
1130
     */
1131
    public void reProject(ICoordTrans arg0) {
1132
        // TODO implementar reprojecci?n (lo que sea eso)
1133
    }
1134

    
1135
    public Envelope getSelectionBounds() throws BaseException {
1136

    
1137
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1138

    
1139
        layers.accept(visitor);
1140
        Envelope env_in_data_crs = visitor.getSelectioEnvelope();
1141
        return env_in_data_crs;
1142
    }
1143

    
1144
    /**
1145
     * <p>
1146
     * Draws this map if its {@link ViewPort ViewPort} has an extent
1147
     * defined:<br>
1148
     * <ol>
1149
     * <li>Selects only the layers that have to be drawn:
1150
     * {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
1151
     * <li>Sets quality: antialiasing by text and images, and quality rendering.
1152
     * <li>Draws the layers.
1153
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
1154
     * <li>Draws the graphic layer.
1155
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
1156
     * <li>Invokes the garbage collector and memory clean.
1157
     * </ol>
1158
     * </p>
1159
     *
1160
     * @param image buffer used sometimes instead <code>g</code> to accelerate
1161
     * the draw. For example, if two points are as closed that can't be
1162
     * distinguished, draws only one.
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 the scale of the view. Must be between
1168
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1169
     * @throws MapContextException if there is an error getting the instance of
1170
     * MapContextDrawer
1171
     * @throws ReadDriverException if fails reading with the driver.
1172
     */
1173
    public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
1174
            double scale) throws ReadException, MapContextException {
1175
        if (viewPort.getEnvelope() == null) {
1176
            return;
1177
        }
1178

    
1179
        CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1180

    
1181
        this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1182
    }
1183

    
1184
    /**
1185
     * <p>
1186
     * Draws only the internal graphic layer using the information of the
1187
     * {@link ViewPort ViewPort} of this map.</p>
1188
     *
1189
     * @param image image used to accelerate the screen draw
1190
     * @param g for rendering 2-dimensional shapes, text and images on the
1191
     * Java(tm) platform
1192
     * @param cancel shared object that determines if this layer can continue
1193
     * being drawn
1194
     * @param scale value that represents the scale
1195
     * @throws ReadDriverException if fails reading with the driver.
1196
     * @deprecated use
1197
     * {@link #draw(BufferedImage, Graphics2D, Cancellable, double)} instead
1198
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
1199
     * double)
1200
     */
1201
    public void drawGraphics(BufferedImage image, Graphics2D g,
1202
            Cancellable cancel, double scale) throws ReadException {
1203

    
1204
                // From now on the graphics layer is handled by the MapContextDrawer,
1205
        // so call the draw method instead.
1206
        try {
1207
            draw(image, g, cancel, scale);
1208
        } catch (MapContextException e) {
1209
            throw new RuntimeException(e);
1210
        }
1211
    }
1212

    
1213
    /**
1214
     * <p>
1215
     * Like
1216
     * {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}
1217
     * , but creating the task as cancellable.
1218
     * </p>
1219
     *
1220
     * @param image buffer used sometimes instead <code>g</code> to accelerate
1221
     * the draw. For example, if two points are as closed that can't be
1222
     * distinguished, draws only one.
1223
     * @param g for rendering 2-dimensional shapes, text and images on the
1224
     * Java(tm) platform
1225
     * @param scale the scale of the view. Must be between
1226
     * {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1227
     * @throws MapContextException if there is an error getting the instance of
1228
     * MapContextDrawer
1229
     *
1230
     * @throws ReadDriverException if the driver fails reading.
1231
     *
1232
     * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1233
     */
1234
    public void draw(BufferedImage image, Graphics2D g, double scale)
1235
            throws ReadException, MapContextException {
1236
        draw(image, g, new Cancellable() {
1237
            /**
1238
             * @see org.gvsig.utils.swing.threads.Cancellable#isCanceled()
1239
             */
1240
            public boolean isCanceled() {
1241
                return false;
1242
            }
1243

    
1244
            public void setCanceled(boolean canceled) {
1245
                // Do nothing
1246
            }
1247
        }, scale);
1248
    }
1249

    
1250
    /**
1251
     * <p>
1252
     * Gets the {@link ViewPort ViewPort} associated to this map.</p>
1253
     *
1254
     * @return the view port
1255
     *
1256
     * @see #setViewPort(ViewPort)
1257
     */
1258
    public ViewPort getViewPort() {
1259
        return viewPort;
1260
    }
1261

    
1262
    /**
1263
     * <p>
1264
     * Sets a {@link ViewPort ViewPort} with the drawing information of this
1265
     * map.</p>
1266
     * <p>
1267
     * If there was a previous view port, removes its
1268
     * {@link EventBuffer EventBuffer} and adds the new one.</p>
1269
     *
1270
     * @param viewPort the viewPort
1271
     *
1272
     * @see #getViewPort()
1273
     */
1274
    public void setViewPort(ViewPort viewPort) {
1275
        if (this.viewPort != null) {
1276
            this.viewPort.removeViewPortListener(eventBuffer);
1277
        }
1278

    
1279
        if (this.mapContextDrawer != null) {
1280
            this.mapContextDrawer.setViewPort(viewPort);
1281
        }
1282

    
1283
        this.viewPort = viewPort;
1284
        if (viewPort != null) {
1285
            viewPort.addViewPortListener(eventBuffer);
1286
        }
1287
    }
1288

    
1289
    /**
1290
     * <p>
1291
     * Sets the given extent to the {@link ViewPort ViewPort} and updates the
1292
     * view with the new zoom.</p>
1293
     *
1294
     * @param extent the extent of the new zoom
1295
     */
1296
    public void zoomToEnvelope(Envelope extent) {
1297
        if (extent != null && !extent.isEmpty()) {
1298
            getViewPort().setEnvelope(extent);
1299
        }
1300
    }
1301

    
1302
    /**
1303
     * <p>
1304
     * Returns the union of all extents of all layers of this map.</p>
1305
     *
1306
     * @return full extent of layers of this map
1307
     * @throws ReadDriverException if the driver fails reading.
1308
     *
1309
     * @see FLayers#getFullEnvelope()
1310
     */
1311
    public Envelope getFullEnvelope() throws ReadException {
1312
        Envelope envelope = layers.getFullEnvelope();
1313

    
1314
        if (tracLayer != null) {
1315
            Envelope graphicsEnvelope = tracLayer.getFullEnvelope();
1316
            if (envelope == null) {
1317
                return graphicsEnvelope;
1318
            } else if (graphicsEnvelope != null) {
1319
                envelope.add(graphicsEnvelope);
1320
            }
1321
        }
1322

    
1323
        return envelope;
1324
    }
1325

    
1326
    /**
1327
     * <p>
1328
     * Adds a listener of atomic events to the internal
1329
     * {@link EventBuffer EventBuffer}.</p>
1330
     *
1331
     * @param listener the new listener
1332
     *
1333
     * @return <code>true</code> if has added the listener successfully
1334
     *
1335
     * @see #removeAtomicEventListener(AtomicEventListener)
1336
     * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1337
     */
1338
    public boolean addAtomicEventListener(AtomicEventListener listener) {
1339
        return eventBuffer.addAtomicEventListener(listener);
1340
    }
1341

    
1342
    /**
1343
     * <p>
1344
     * Removes a listener of atomic events from the internal
1345
     * {@link EventBuffer EventBuffer}.</p>
1346
     *
1347
     * @param listener the listener to remove
1348
     *
1349
     * @return <tt>true</tt> if the list contained the specified element
1350
     *
1351
     * @see #addAtomicEventListener(AtomicEventListener)
1352
     * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1353
     */
1354
    public boolean removeAtomicEventListener(AtomicEventListener listener) {
1355
        return eventBuffer.removeAtomicEventListener(listener);
1356
    }
1357

    
1358
    /**
1359
     * @see EventBuffer#beginAtomicEvent()
1360
     *
1361
     * @see #endAtomicEvent()
1362
     */
1363
    public void beginAtomicEvent() {
1364
        eventBuffer.beginAtomicEvent();
1365
    }
1366

    
1367
    /**
1368
     * @see EventBuffer#endAtomicEvent()
1369
     *
1370
     * @see #beginAtomicEvent()
1371
     */
1372
    public void endAtomicEvent() {
1373
        eventBuffer.endAtomicEvent();
1374
    }
1375

    
1376
    /**
1377
     * <p>
1378
     * The class <code>LayerEventListener</code> implements the methods of
1379
     * {@link LayerCollectionListener LayerCollectionListener} that handles the
1380
     * "layer added" or "layer removed" events in a map.</p>
1381
     * <p>
1382
     * Is designed as a listener for all layers in a
1383
     * {@link MapContext MapContext}.</p>
1384
     *
1385
     * @author Fernando Gonz?lez Cort?s
1386
     */
1387
    public class LayerEventListener implements LayerCollectionListener {
1388
        /*
1389
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1390
         */
1391

    
1392
        public void layerAdded(LayerCollectionEvent e) {
1393
                        // Si es la primera capa, fijamos su extent al ViewPort
1394
            // if (getLayers().getLayersCount() == 1) {
1395
            if (getViewPort().getEnvelope() == null) {
1396
                FLayer lyr = e.getAffectedLayer();
1397
                if (lyr.isAvailable()) {
1398
                    try {
1399
                        getViewPort().setEnvelope(lyr.getFullEnvelope());
1400
                    } catch (ReadException ex) {
1401
                        logger.error(
1402
                                MessageFormat.format(
1403
                                        "Can't set envelope to view port from layer {0}",
1404
                                        new Object[]{lyr.getName()}
1405
                                ),
1406
                                ex
1407
                        );
1408
                    }
1409
                }
1410
            }
1411

    
1412
            // Registramos al FMap como listener del legend de las capas
1413
            FLayer lyr = e.getAffectedLayer();
1414
            addSelectionListener(lyr);
1415
        }
1416

    
1417
        /*
1418
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1419
         */
1420
        public void layerMoved(LayerPositionEvent e) {
1421
        }
1422

    
1423
        /*
1424
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1425
         */
1426
        public void layerRemoved(LayerCollectionEvent e) {
1427
            FLayer lyr = e.getAffectedLayer();
1428

    
1429
            lyr.removeLayerListener(eventBuffer);
1430

    
1431
            if (lyr instanceof Classifiable) {
1432
                Classifiable c = (Classifiable) lyr;
1433
                c.removeLegendListener(eventBuffer);
1434
            }
1435

    
1436
            if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore() != null) {
1437
                ((SingleLayer) lyr).getDataStore().deleteObserver(
1438
                        MapContext.this);
1439
            }
1440

    
1441
        }
1442

    
1443
        /*
1444
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1445
         */
1446
        public void layerAdding(LayerCollectionEvent e)
1447
                throws CancelationException {
1448
        }
1449

    
1450
        /*
1451
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1452
         */
1453
        public void layerMoving(LayerPositionEvent e)
1454
                throws CancelationException {
1455
        }
1456

    
1457
        /*
1458
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1459
         */
1460
        public void layerRemoving(LayerCollectionEvent e)
1461
                throws CancelationException {
1462
        }
1463

    
1464

    
1465
        /*
1466
         * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1467
         */
1468
        public void visibilityChanged(LayerCollectionEvent e)
1469
                throws CancelationException {
1470
        }
1471
    }
1472

    
1473
    /**
1474
     * <p>
1475
     * Adds the {@link LayerEventListener LayerEventListener} of this map to the
1476
     * collection of layers argument.</p>
1477
     *
1478
     * @param a collection of layers
1479
     */
1480
    public void addAsCollectionListener(FLayers layers2) {
1481
        layers2.addLayerCollectionListener(layerEventListener);
1482
    }
1483

    
1484
    /**
1485
     * <p>
1486
     * Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1487
     *
1488
     * @return the graphic layer of this map
1489
     *
1490
     * @see #setGraphicsLayer(GraphicLayer)
1491
     */
1492
    public GraphicLayer getGraphicsLayer() {
1493
        if (tracLayer == null) {
1494
            if (getViewPort() != null) {
1495
                this.tracLayer
1496
                        = MapContextLocator.getMapContextManager()
1497
                        .createGraphicsLayer(
1498
                                getViewPort().getProjection());
1499
            } else {
1500
                this.tracLayer
1501
                        = MapContextLocator.getMapContextManager()
1502
                        .createGraphicsLayer(null);
1503
            }
1504
        }
1505
        return tracLayer;
1506
    }
1507

    
1508
    /**
1509
     * <p>
1510
     * Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1511
     *
1512
     * @param graphicLayer the new graphic layer
1513
     *
1514
     * @see #getGraphicsLayer()
1515
     */
1516
    public void setGraphicsLayer(GraphicLayer graphicLayer) {
1517
        tracLayer = graphicLayer;
1518
    }
1519

    
1520
    /**
1521
     * <p>
1522
     * Indicates whether some other object is "equal to" this map.</p>
1523
     * <p>
1524
     * Returns <code>true</code> if success one of this options:
1525
     * <ul>
1526
     * <li>Both objects are equal according to
1527
     * {@linkplain Object#equals(Object)}.
1528
     * <li>Both maps have the same layers.
1529
     * <li>Both maps have the same number of layers and with the same name.
1530
     * </ul>
1531
     * </p>
1532
     *
1533
     * @param obj the reference object with which to compare.
1534
     * @return <code>true</code> if this object is the same as the
1535
     * <code>arg0</code> argument; otherwise <code>false</code>.
1536
     *
1537
     * @see Object#equals(Object)
1538
     */
1539
    public boolean equals(Object arg0) {
1540
        if (!(arg0 instanceof MapContext)) {
1541
            return false;
1542
        }
1543
        MapContext map = (MapContext) arg0;
1544
        if (super.equals(arg0)) {
1545
            return true;
1546
        }
1547
        if (getLayers() == map.getLayers()) {
1548
            return true;
1549
        }
1550
        boolean isEqual = true;
1551
        if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1552
            for (int i = 0; i < getLayers().getLayersCount(); i++) {
1553

    
1554
                if (!getLayers().getLayer(i).getName().equals(
1555
                        map.getLayers().getLayer(i).getName())) {
1556
                    isEqual = false;
1557
                }
1558

    
1559
            }
1560
        } else {
1561
            isEqual = false;
1562
        }
1563
        return isEqual;
1564
    }
1565

    
1566
    /**
1567
     * <p>
1568
     * Registers the message of an error associated to this map.</p>
1569
     *
1570
     * @param stringProperty the error message
1571
     *
1572
     * @see #getLayersError()
1573
     * @see #clearErrors()
1574
     */
1575
    public void addLayerError(String stringProperty) {
1576
        layersError.add(stringProperty);
1577
    }
1578

    
1579
    /**
1580
     * <p>
1581
     * Gets the list with all error messages registered to this map.</p>
1582
     *
1583
     * @return the list of errors registered to this map
1584
     *
1585
     * @see #addLayerError(String)
1586
     * @see #clearErrors()
1587
     */
1588
    public ArrayList getLayersError() {
1589
        return layersError;
1590
    }
1591

    
1592
    /**
1593
     * <p>
1594
     * Removes all error messages associated to this map.</p>
1595
     *
1596
     * @see #addLayerError(String)
1597
     * @see #getLayersError()
1598
     */
1599
    public void clearErrors() {
1600
        layersError.clear();
1601
    }
1602

    
1603
    /**
1604
     * <p>
1605
     * Creates and returns a new group of layers that belongs to this
1606
     * <code>MapContext</code>.</p>
1607
     *
1608
     * @param parent layer node in this <code>MapContexte</code> that will be
1609
     * the parent of the new node
1610
     * @return the new layer node
1611
     */
1612
    public FLayers getNewGroupLayer(FLayers parent) {
1613
        FLayers group1 = new FLayers();//(this,parent);
1614
        group1.setMapContext(this);
1615
        group1.setParentLayer(parent);
1616
        return group1;
1617
    }
1618

    
1619
    public String getClassName() {
1620
        return null;
1621
    }
1622

    
1623
    public ArrayList getLayersToSnap() {
1624
        return layersToSnap;
1625
    }
1626

    
1627
    public void setLayersToSnap(ArrayList layersToSnap) {
1628
        this.layersToSnap = layersToSnap;
1629

    
1630
    }
1631

    
1632
    public void update(Observable observable, Object notification) {
1633
        // TODO REVISAR ESTO!!!
1634
        String ntype = null;
1635
        if (notification instanceof FeatureStoreNotification) {
1636
            FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1637
            ntype = fsNotification.getType();
1638
            if (ntype.equals(FeatureStoreNotification.LOAD_FINISHED)
1639
                    || ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)) {
1640
                getLayers().moveTo(0, 0);
1641
            }
1642
        }
1643
    }
1644

    
1645
    public long getDrawVersion() {
1646
        if (getViewPort().getDrawVersion() > this.viewPortVersion
1647
                || getLayers().getDrawVersion() > this.layersVersion
1648
                || getGraphicsLayer().getDrawVersion() > graphicsLayerVersion) {
1649
            updateDrawVersion();
1650
        }
1651
        return this.drawVersion;
1652
    }
1653

    
1654
    protected void updateDrawVersion() {
1655
        this.layersVersion = getLayers().getDrawVersion();
1656
        this.viewPortVersion = getViewPort().getDrawVersion();
1657
        this.graphicsLayerVersion = getGraphicsLayer().getDrawVersion();
1658
        this.drawVersion++;
1659
    }
1660

    
1661
    public MapContextDrawer getMapContextDrawer() throws ReadException,
1662
            MapContextException {
1663
        if (this.mapContextDrawer == null) {
1664
            if (mapContextDrawerClass == null) {
1665
                this.mapContextDrawer = mapContextManager
1666
                        .createDefaultMapContextDrawerInstance();
1667
            } else {
1668
                this.mapContextDrawer = mapContextManager
1669
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1670
            }
1671
            this.mapContextDrawer.setMapContext(this);
1672
            this.mapContextDrawer.setViewPort(viewPort);
1673
        }
1674

    
1675
        return this.mapContextDrawer;
1676
    }
1677

    
1678
    public void setMapContextDrawerClass(Class mapContextDrawerClass)
1679
            throws MapContextException {
1680
        mapContextManager.validateMapContextDrawer(mapContextDrawerClass);
1681
        this.mapContextDrawerClass = mapContextDrawerClass;
1682
        if (this.mapContextDrawer != null) {
1683
            this.mapContextDrawer.dispose();
1684
            this.mapContextDrawer = null;
1685
        }
1686
    }
1687

    
1688
    public void setMapContextDrawer(MapContextDrawer drawer) {
1689
        if (this.mapContextDrawer != null) {
1690
            this.mapContextDrawer.dispose();
1691
            this.mapContextDrawer = null;
1692
        }
1693
        this.mapContextDrawer = drawer;
1694
        if (this.mapContextDrawer != null) {
1695
            this.mapContextDrawer.setMapContext(this);
1696
            this.mapContextDrawer.setViewPort(viewPort);
1697
        }
1698
    }
1699

    
1700
    public void loadFromState(PersistentState state)
1701
            throws PersistenceException {
1702

    
1703
        ViewPort vp = (ViewPort) state.get("ViewPort");
1704
        setViewPort(vp);
1705

    
1706
        layers = (FLayers) state.get("layers");
1707
        layers.setName("root layer");
1708
        loadLayers(layers);
1709
        layers.setMapContext(this);
1710

    
1711
        layerEventListener = new LayerEventListener();
1712
        layers.addLayerCollectionListener(layerEventListener);
1713

    
1714
        layers.addLayerCollectionListener(eventBuffer);
1715
        layers.setProjection(vp.getProjection());
1716

    
1717
        //Add the listener for the selection
1718
        addSelectionListener(layers);
1719

    
1720
        // ======================
1721
        if (state.hasValue("orderManager")) {
1722
            LayerOrderManager lom = (LayerOrderManager) state.get("orderManager");
1723
            this.setOrderManager(lom);
1724
        }
1725
        DefaultMapContextManager manager = (DefaultMapContextManager) MapContextLocator.getMapContextManager();
1726
        manager.notifyLoadMapContext(this);
1727
    }
1728

    
1729
    private void loadLayers(FLayers lyrs) {
1730

    
1731
        int sz = lyrs.getLayersCount();
1732
        for (int i = 0; i < sz; i++) {
1733
            try {
1734
                lyrs.getLayer(i).load();
1735
            } catch (LoadLayerException e) {
1736
                logger.error("While loading layer: " + lyrs.getLayer(i).getName());
1737
            }
1738
        }
1739
    }
1740

    
1741
    public void saveToState(PersistentState state) throws PersistenceException {
1742
        state.set("ViewPort", viewPort);
1743
        state.set("layers", layers);
1744
        state.set("orderManager", this.getOrderManager());
1745
    }
1746

    
1747
    public static class RegisterPersistence implements Callable {
1748

    
1749
        public Object call() {
1750
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
1751
            DynStruct definition = manager.addDefinition(
1752
                    MapContext.class,
1753
                    "MapContext",
1754
                    "MapContext Persistence definition",
1755
                    null,
1756
                    null
1757
            );
1758
            definition.addDynFieldObject("ViewPort")
1759
                    .setClassOfValue(ViewPort.class)
1760
                    .setMandatory(true);
1761

    
1762
            definition.addDynFieldObject("layers")
1763
                    .setClassOfValue(FLayers.class)
1764
                    .setMandatory(true);
1765

    
1766
            definition.addDynFieldObject("orderManager")
1767
                    .setClassOfValue(LayerOrderManager.class)
1768
                    .setMandatory(false);
1769

    
1770
            return Boolean.TRUE;
1771
        }
1772
    }
1773

    
1774
    protected void doDispose() throws BaseException {
1775
        dispose(layers);
1776
        dispose(tracLayer);
1777
    }
1778

    
1779
    /**
1780
     * <p>
1781
     * Registers an event buffer as a listener for all layers as argument.</p>
1782
     *
1783
     * <p>
1784
     * Each {@link FLayer FLayer} of this map must have an event buffer for all
1785
     * kind of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1786
     * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers}
1787
     * layers, and for each one, registers, for their specific listeners, the
1788
     * <code>eventBuffer</code> as a listener.</p>
1789
     *
1790
     * @param the layer or layers
1791
     */
1792
    private void addSelectionListener(FLayer lyr) {
1793
        lyr.addLayerListener(eventBuffer);
1794

    
1795
        if (lyr instanceof Classifiable) {
1796
            Classifiable c = (Classifiable) lyr;
1797
            c.addLegendListener(eventBuffer);
1798
        }
1799

    
1800
        if (lyr instanceof FLayers) {
1801
            FLayers lyrs = (FLayers) lyr;
1802
            for (int i = 0; i < lyrs.getLayersCount(); i++) {
1803
                addSelectionListener(lyrs.getLayer(i));
1804
            }
1805
        }
1806
        if (lyr instanceof SingleLayer) {
1807
            if (((SingleLayer) lyr).getDataStore() != null) {
1808
                ((SingleLayer) lyr).getDataStore().addObserver(
1809
                        MapContext.this);
1810
            }
1811
        }
1812
    }
1813

    
1814
    public void setOrderManager(LayerOrderManager lom) {
1815
        orderManager = lom;
1816
    }
1817

    
1818
    public LayerOrderManager getOrderManager() {
1819

    
1820
        if (orderManager == null) {
1821
            orderManager = MapContextLocator.getDefaultOrderManager();
1822
        }
1823
        return orderManager;
1824
    }
1825

    
1826
    public boolean hasVectorLayers() {
1827
        return this.hasVectorLayers(this.getLayers());
1828
    }
1829

    
1830
    public boolean hasActiveVectorLayers() {
1831
        FLayer[] layers = this.getLayers().getActives();
1832
        for (int i = 0; i < layers.length; i++) {
1833
            FLayer layer = layers[i];
1834
            if (layer.isAvailable() && layer instanceof FLyrVect) {
1835
                return true;
1836
            }
1837
        }
1838
        return false;
1839
    }
1840

    
1841
    private boolean hasVectorLayers(FLayers layers) {
1842
        for (int i = 0; i < layers.getLayersCount(); i++) {
1843
            FLayer lyr = layers.getLayer(i);
1844
            if (lyr instanceof FLayers) {
1845
                if (hasVectorLayers((FLayers) lyr)) {
1846
                    return true;
1847
                }
1848
            } else if (lyr instanceof FLyrVect) {
1849
                return true;
1850
            }
1851
        }
1852
        return false;
1853
    }
1854
}