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

History | View | Annotate | Download (62.9 KB)

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

    
25
import java.awt.Color;
26
import java.awt.Graphics;
27
import java.awt.Graphics2D;
28
import java.awt.geom.Rectangle2D;
29
import java.awt.image.BufferedImage;
30
import java.text.MessageFormat;
31
import java.util.ArrayList;
32
import java.util.Iterator;
33
import java.util.List;
34

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

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

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

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

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

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

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

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

    
190
    }
191

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
390
    public static final int DISJOINT = 1;
391

    
392
    public static final int INTERSECTS = 2;
393

    
394
    public static final int TOUCHES = 3;
395

    
396
    public static final int CROSSES = 4;
397

    
398
    public static final int WITHIN = 5;
399

    
400
    public static final int CONTAINS = 6;
401

    
402
    public static final int OVERLAPS = 7;
403

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
585
    public MapContext() {
586
    }
587

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

    
601
        layerEventListener = new LayerEventListener();
602

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

    
609
        setViewPort(vp);
610
    }
611

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
894
        return ret;
895
    }
896

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

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

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

    
948
    }
949

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

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

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

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

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

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

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

    
1018
    }
1019

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

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

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

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

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

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

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

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

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

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

    
1136
    public Envelope getSelectionBounds() throws BaseException {
1137

    
1138
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1139

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1324
        return envelope;
1325
    }
1326

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

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

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

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

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

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

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

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

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

    
1430
            lyr.removeLayerListener(eventBuffer);
1431

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

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

    
1442
        }
1443

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

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

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

    
1465

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1631
    }
1632

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

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

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

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

    
1676
        return this.mapContextDrawer;
1677
    }
1678

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

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

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

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

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

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

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

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

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

    
1730
    private void loadLayers(FLayers lyrs) {
1731

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

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

    
1748
    public static class RegisterPersistence implements Callable {
1749

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

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

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

    
1771
            return Boolean.TRUE;
1772
        }
1773
    }
1774

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

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

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

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

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

    
1819
    public LayerOrderManager getOrderManager() {
1820

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

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

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

    
1842
    private boolean hasVectorLayers(FLayers layers) {
1843
        for (int i = 0; i < layers.getLayersCount(); i++) {
1844
            FLayer lyr = layers.getLayer(i);
1845
            if (lyr instanceof FLayers) {
1846
                if (hasVectorLayers((FLayers) lyr)) {
1847
                    return true;
1848
                }
1849
            } else if (lyr instanceof FLyrVect) {
1850
                return true;
1851
            }
1852
        }
1853
        return false;
1854
    }
1855
    
1856
    @Override
1857
    public Iterator<FLayer> iterator() {
1858
        return this.layers.iterator();
1859
    }
1860

    
1861
    public Iterator deepiterator() {
1862
        return this.layers.deepiterator();
1863
    }
1864
 
1865
}