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

History | View | Annotate | Download (63.2 KB)

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

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

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

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

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

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

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

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

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

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

    
191
    }
192

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
391
    public static final int DISJOINT = 1;
392

    
393
    public static final int INTERSECTS = 2;
394

    
395
    public static final int TOUCHES = 3;
396

    
397
    public static final int CROSSES = 4;
398

    
399
    public static final int WITHIN = 5;
400

    
401
    public static final int CONTAINS = 6;
402

    
403
    public static final int OVERLAPS = 7;
404

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
586
    public MapContext() {
587
    }
588

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

    
602
        layerEventListener = new LayerEventListener();
603

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

    
610
        setViewPort(vp);
611
    }
612

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
895
        return ret;
896
    }
897

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

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

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

    
949
    }
950

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

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

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

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

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

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

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

    
1019
    }
1020

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

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

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

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

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

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

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

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

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

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

    
1137
    public Envelope getSelectionBounds() throws BaseException {
1138

    
1139
        SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1140

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1325
        return envelope;
1326
    }
1327

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

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

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

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

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

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

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

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

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

    
1431
            lyr.removeLayerListener(eventBuffer);
1432

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

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

    
1443
        }
1444

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

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

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

    
1466

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1632
    }
1633

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

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

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

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

    
1677
        return this.mapContextDrawer;
1678
    }
1679

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

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

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

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

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

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

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

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

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

    
1731
    private void loadLayers(FLayers lyrs) {
1732

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

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

    
1749
    public static class RegisterPersistence implements Callable {
1750

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

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

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

    
1772
            return Boolean.TRUE;
1773
        }
1774
    }
1775

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

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

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

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

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

    
1820
    public LayerOrderManager getOrderManager() {
1821

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

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

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

    
1843
    public boolean hasActiveLayers() {
1844
        FLayer[] layers = this.getLayers().getActives();
1845
        return !ArrayUtils.isEmpty(layers);
1846
    }
1847

    
1848
    public boolean hasLayers() {
1849
        return !this.getLayers().isEmpty();
1850
    }
1851
    
1852
    private boolean hasVectorLayers(FLayers layers) {
1853
        for (int i = 0; i < layers.getLayersCount(); i++) {
1854
            FLayer lyr = layers.getLayer(i);
1855
            if (lyr instanceof FLayers) {
1856
                if (hasVectorLayers((FLayers) lyr)) {
1857
                    return true;
1858
                }
1859
            } else if (lyr instanceof FLyrVect) {
1860
                return true;
1861
            }
1862
        }
1863
        return false;
1864
    }
1865
    
1866
    @Override
1867
    public Iterator<FLayer> iterator() {
1868
        return this.layers.iterator();
1869
    }
1870

    
1871
    public Iterator deepiterator() {
1872
        return this.layers.deepiterator();
1873
    }
1874
 
1875
}