Statistics
| Revision:

svn-gvsig-desktop / branches / v2_0_0_prep / libraries / libFMap_mapcontext / src / org / gvsig / fmap / mapcontext / MapContext.java @ 33281

History | View | Annotate | Download (52.7 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
package org.gvsig.fmap.mapcontext;
42

    
43
import java.awt.Color;
44
import java.awt.Graphics;
45
import java.awt.Graphics2D;
46
import java.awt.geom.Rectangle2D;
47
import java.awt.image.BufferedImage;
48
import java.util.ArrayList;
49
import java.util.List;
50

    
51
import org.cresques.cts.ICoordTrans;
52
import org.cresques.cts.IProjection;
53
import org.cresques.geo.Projected;
54
import org.gvsig.compat.CompatLocator;
55
import org.gvsig.compat.print.PrintAttributes;
56
import org.gvsig.fmap.dal.exception.ReadException;
57
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
58
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
59
import org.gvsig.fmap.geom.GeometryLocator;
60
import org.gvsig.fmap.geom.GeometryManager;
61
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
62
import org.gvsig.fmap.geom.primitive.Envelope;
63
import org.gvsig.fmap.mapcontext.MapContext.LayerEventListener;
64
import org.gvsig.fmap.mapcontext.events.ErrorEvent;
65
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
66
import org.gvsig.fmap.mapcontext.events.listeners.ErrorListener;
67
import org.gvsig.fmap.mapcontext.events.listeners.EventBuffer;
68
import org.gvsig.fmap.mapcontext.exceptions.LoadLayerException;
69
import org.gvsig.fmap.mapcontext.layers.CancelationException;
70
import org.gvsig.fmap.mapcontext.layers.FLayer;
71
import org.gvsig.fmap.mapcontext.layers.FLayers;
72
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
73
import org.gvsig.fmap.mapcontext.layers.LayerCollectionListener;
74
import org.gvsig.fmap.mapcontext.layers.LayerDrawEvent;
75
import org.gvsig.fmap.mapcontext.layers.LayerDrawingListener;
76
import org.gvsig.fmap.mapcontext.layers.LayerPositionEvent;
77
import org.gvsig.fmap.mapcontext.layers.operations.Classifiable;
78
import org.gvsig.fmap.mapcontext.layers.operations.SingleLayer;
79
import org.gvsig.fmap.mapcontext.layers.vectorial.GraphicLayer;
80
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
81
import org.gvsig.fmap.mapcontext.rendering.strategies.SelectedZoomVisitor;
82
import org.gvsig.tools.ToolsLocator;
83
import org.gvsig.tools.dispose.impl.AbstractDisposable;
84
import org.gvsig.tools.dynobject.DynStruct;
85
import org.gvsig.tools.exception.BaseException;
86
import org.gvsig.tools.observer.Observable;
87
import org.gvsig.tools.observer.Observer;
88
import org.gvsig.tools.persistence.PersistenceManager;
89
import org.gvsig.tools.persistence.Persistent;
90
import org.gvsig.tools.persistence.PersistentState;
91
import org.gvsig.tools.persistence.exception.PersistenceException;
92
import org.gvsig.tools.task.Cancellable;
93
import org.gvsig.tools.visitor.Visitor;
94
import org.slf4j.Logger;
95
import org.slf4j.LoggerFactory;
96

    
97

    
98
/**
99
 * <p>The <code>MapContext</code> class represents the model and a part of the control and view around graphical layers
100
 * used by {@link MapControl MapControl}.</p>
101
 *
102
 * <p>An instance of <code>MapContext</code> is made up with:
103
 * <ul>
104
 * <li>a hierarchy of {@link FLayers FLayers} nodes
105
 * <li>a {@link GraphicLayer GraphicLayer} layer
106
 * <li>a {@link ViewPort ViewPort}
107
 * <li>an {@link EventBuffer EventButter}
108
 * <li>some {@link com.iver.cit.gvsig.fmap.layers.LegendListener LegendListener}s
109
 * <li>some {@link LayerDrawingListener LayerDrawingListener}s
110
 * <li>some {@link LayerEventListener LayerEventListener}s
111
 * <li>some {@link ErrorListener ErrorListener}s
112
 * </ul>
113
 * </p>
114
 *
115
 * @author Fernando Gonz?lez Cort?s
116
 */
117
public class MapContext extends AbstractDisposable implements Projected,
118
                Persistent, Observer {
119
        /**
120
         * <p>Defines the value which a unit of a distance measurement must be divided to obtain its equivalent <b>in meters</b>.</p>
121
         *
122
         * <p><b><i>Conversion values of distance measurements:</i></b>
123
         * <ul>
124
         *  <li><code>MapContext.CHANGEM[0]</code>: kilometer
125
         *  <li><code>MapContext.CHANGEM[1]</code>: meter
126
         *  <li><code>MapContext.CHANGEM[2]</code>: centimeter
127
         *  <li><code>MapContext.CHANGEM[3]</code>: millimeter
128
         *  <li><code>MapContext.CHANGEM[4]</code>: international statute mile
129
         *  <li><code>MapContext.CHANGEM[5]</code>: yard
130
         *  <li><code>MapContext.CHANGEM[6]</code>: foot
131
         *  <li><code>MapContext.CHANGEM[7]</code>: inch
132
         *  <li><code>MapContext.CHANGEM[8]</code>: grade
133
         * </ul>
134
         *
135
         * <p><h3>Examples:</h3>
136
         * <pre>1 international statute mile / MapContext.CHANGEM[4] = X meters</pre>
137
         * <pre>1 kilometer / MapContext.CHANGEM[0] = X meters</pre>
138
         * <pre>1 grade / MapContext.CHANGEM[8] = X meters</pre>
139
         * </p>
140
         *
141
         * <p><h3>Grade conversion value: <code>MapContext.CHANGEM[8]</code></h3>
142
         * The value of <code>MapContext.CHANGEM[8]</code> represents the meters of a straight line between two
143
         *  points on the Earth surface that are 1 grade far each other of the center of the Earth. This value has been calculated using
144
         *  a radius approximated of R<sub>Earth</sub>=6.37846082678100774672e6 meters, according these equations:
145
         * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
146
         * <pre>MapContext.CHANGEM[8] = 1 / D</pre>
147
         * <h4>Explanation:</h4>
148
         * We get an isosceles triangle with the center of the Earth and the 2 points on the surface. This triangle can be divided into
149
         * two rectangle triangles. We know two values, the angle of 1 grade, that will be 0.50 grades in each triangle, and the Earth radius that
150
         * is the hypotenuse. Then we apply trigonometry and get the distance <i>D</i> between both points on the Earth surface.</p>
151
         * <p>Now we only must invert that value to obtain <code>MapContext.CHANGEM[8]</code>.</p>
152
         *@deprecated use getDistanceTrans2Meter()
153
         */
154
        public static final double[] CHANGEM = { 1000, 1, 0.01, 0.001, 1609.344,
155
                        0.9144, 0.3048, 0.0254, 1/8.983152841195214E-6 };
156

    
157

    
158
        public static ArrayList AREANAMES=new ArrayList();
159
        public static ArrayList AREAABBR=new ArrayList();
160
        public static ArrayList AREATRANS2METER=new ArrayList();
161

    
162
        public static ArrayList DISTANCENAMES=new ArrayList();
163
        public static ArrayList DISTANCEABBR=new ArrayList();
164
        public static ArrayList DISTANCETRANS2METER=new ArrayList();
165

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

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

    
187

    
188
    }
189
        
190
        private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
191
        
192
        private static final MapContextManager mapContextManager = MapContextLocator
193
            .getMapContextManager(); 
194
        
195
        private static final Logger logger = LoggerFactory.getLogger(GeometryManager.class);
196

    
197
        /**
198
         * <p>Determines the number of frames.</p>
199
         *
200
         * <p>Number of updates per second that the timer will invoke repaint this component.</p>
201
         */
202
    private static int drawFrameRate = 3;
203
    /**
204
         * <p>Returns the draw frame rate.</p>
205
         *
206
         * <p>Draw frame rate is the number of repaints of this <code>MapControl</code> instance that timer invokes per second.</p>
207
         *
208
         * @return number of repaints of this <code>MapControl</code> instance that timer invokes per second
209
         *
210
         * @see #applyFrameRate()
211
         * @see #setDrawFrameRate(int)
212
         */
213
        public static int getDrawFrameRate() {
214
                return drawFrameRate;
215
        }
216

    
217
        /**
218
         * <p>Sets the draw frame rate.</p>
219
         *
220
         * <p>Draw frame rate is the number of repaints of this <code>MapControl</code> instance that timer invokes per second.</p>
221
         *
222
         * @param drawFrameRate number of repaints of this <code>MapControl</code> instance that timer invokes per second
223
         *
224
         * @see #applyFrameRate()
225
         * @see #getDrawFrameRate()
226
         */
227
        public static void setDrawFrameRate(int dFR) {
228
                drawFrameRate = dFR;
229
        }
230
        public static void addAreaUnit(String name, String abbr,boolean isLinear,double trans2meter){
231
                if (!AREANAMES.contains(name)){
232
                        AREANAMES.add(name);
233
                        String pow="";
234
                        if (isLinear) {
235
                                pow=String.valueOf((char)178);
236
                        }
237
                        AREAABBR.add(abbr+pow);
238
                        AREATRANS2METER.add(new Double(trans2meter));
239
                }
240
        }
241
        public static String[] getAreaNames(){
242
                return (String[])AREANAMES.toArray(new String[0]);
243
        }
244
        public static String[] getAreaAbbr(){
245
                return (String[])AREAABBR.toArray(new String[0]);
246
        }
247
        public static double[] getAreaTrans2Meter(){
248
                int size=AREATRANS2METER.size();
249
                double[] trans2meters=new double[size];
250
                for (int i = 0; i < size; i++) {
251
                        trans2meters[i]=((Double)AREATRANS2METER.get(i)).doubleValue();
252
                }
253
                return trans2meters;
254
        }
255
        public static String getOfLinear(int i) {
256
                if (((String)AREAABBR.get(i)).toLowerCase().endsWith(String.valueOf((char)178))){
257
                        return String.valueOf((char)178);
258
                }
259
                return "";
260
        }
261
        public static void addDistanceUnit(String name, String abbr,double trans2meter){
262
                if (!DISTANCENAMES.contains(name)){
263
                        DISTANCENAMES.add(name);
264
                        DISTANCEABBR.add(abbr);
265
                        DISTANCETRANS2METER.add(new Double(trans2meter));
266
                }
267
        }
268
        public static String[] getDistanceNames(){
269
                return (String[])DISTANCENAMES.toArray(new String[0]);
270
        }
271
        
272
        public String getDistanceName() {
273
                return (String) DISTANCENAMES.get( this.getViewPort().getDistanceUnits() );
274
        }
275
        
276
        public static String[] getDistanceAbbr(){
277
                return (String[])DISTANCEABBR.toArray(new String[0]);
278
        }
279
        public static double[] getDistanceTrans2Meter(){
280
                int size=DISTANCETRANS2METER.size();
281
                double[] trans2meters=new double[size];
282
                for (int i = 0; i < size; i++) {
283
                        trans2meters[i]=((Double)DISTANCETRANS2METER.get(i)).doubleValue();
284
                }
285
                return trans2meters;
286
        }
287
        public static int getDistancePosition(String s){
288
                for (int i = 0; i < DISTANCENAMES.size(); i++) {
289
                        if (DISTANCENAMES.get(i).equals(s)){
290
                                return i;
291
                        }
292
                }
293
                return 0;
294
        }
295

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

    
334
        /* Do not alter the order and the values of this array, if you need append values.*/
335
        /**
336
         * <p>Gets the name of all distance measurements supported by <code>MapContext</code>.</p>
337
         */
338
//        public static final String[] NAMES= {
339
//                Messages.getString("Kilometros"),
340
//                Messages.getString("Metros"),
341
//                Messages.getString("Centimetros"),
342
//                Messages.getString("Milimetros"),
343
//                Messages.getString("Millas"),
344
//                Messages.getString("Yardas"),
345
//                Messages.getString("Pies"),
346
//                Messages.getString("Pulgadas"),
347
//                Messages.getString("Grados"),
348
//        };
349

    
350
        public static final int EQUALS = 0;
351

    
352
        public static final int DISJOINT = 1;
353

    
354
        public static final int INTERSECTS = 2;
355

    
356
        public static final int TOUCHES = 3;
357

    
358
        public static final int CROSSES = 4;
359

    
360
        public static final int WITHIN = 5;
361

    
362
        public static final int CONTAINS = 6;
363

    
364
        public static final int OVERLAPS = 7;
365

    
366
        /**
367
         * A hierarchy of {@link FLayers FLayers} nodes.
368
         *
369
         * @see #getLayers()
370
         * @see #print(Graphics2D, double, PrintAttributes)
371
         */
372
        protected FLayers layers;
373

    
374
        /**
375
         * A layer with graphical items: geometries and symbols.
376
         *
377
         * @see #getGraphicsLayer()
378
         * @see #setGraphicsLayer(GraphicLayer)
379
         * @see #drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
380
         * @see #print(Graphics2D, double, PrintAttributes)
381
         */
382
        private GraphicLayer tracLayer = null;
383
                //MapContextLocator.getMapContextManager().createGraphicsLayer(getProjection());
384

    
385
        /**
386
         * Information for draw layers in a view.
387
         *
388
         * @see #getViewPort()
389
         * @see #setViewPort(ViewPort)
390
         */
391
        private ViewPort viewPort;
392

    
393
        // private ArrayList invalidationListeners = new ArrayList();
394

    
395
        /**
396
         * Array list with all {@link LegendListener LegendListener} registered to this map.
397
         *
398
         * @see #addLayerListener(LegendListener)
399
         * @see #removeLayerListener(LegendListener)
400
         * @see #callLegendChanged()
401
         */
402
        private ArrayList legendListeners = new ArrayList();
403

    
404
        /**
405
         * Array list with all {@link LayerDrawingListener LayerDrawingListener} registered to this map.
406
         *
407
         * @see #addLayerDrawingListener(LayerDrawingListener)
408
         * @see #removeLayerDrawListener(LayerDrawingListener)
409
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
410
         */
411
        private ArrayList layerDrawingListeners = new ArrayList();
412

    
413
        /**
414
         * <p>Buffer that is used to store and eject events produced on this map:
415
         * <ul>
416
         *  <li>Layer collection events.
417
         *  <li>View port events.
418
         *  <li>Atomic events.
419
         *  <li>Layer events.
420
         *  <li>Legend events on a {@link Classificable Classificable} layer.
421
         *  <li>Selection events on an {@link AlphanumericData AlphanumericData} data layer.
422
         * </ul>
423
         * </p>
424
         *
425
         * @see #addAtomicEventListener(AtomicEventListener)
426
         * @see #removeAtomicEventListener(AtomicEventListener)
427
         * @see #beginAtomicEvent()
428
         * @see #endAtomicEvent()
429
         */
430
        private EventBuffer eventBuffer = new EventBuffer();
431

    
432
        /**
433
         * Event listener for the collection of layers of this map.
434
         */
435
        private LayerEventListener layerEventListener = null;
436

    
437
        /**
438
         * List with information of all errors produced on all layers.
439
         *
440
         * @see #addLayerError(String)
441
         * @see #getLayersError()
442
         * @see #clearErrors()
443
         */
444
        private ArrayList layersError = new ArrayList();
445

    
446
        /**
447
         * Array list with all {@link ErrorListener ErrorListener} registered to this map.
448
         *
449
         * @see #addErrorListener(ErrorListener)
450
         * @see #removeErrorListener(LegendListener)
451
         * @see #reportDriverExceptions(String, List)
452
         */
453
        private ArrayList errorListeners = new ArrayList();
454

    
455

    
456

    
457
        // public static ResourceBundle myResourceBundle =
458
        // ResourceBundle.getBundle("FMap");
459

    
460
        /**
461
         * <p>Default <i>zoom in</i> factor.</p>
462
         * <p>Doing a <i>zoom in</i> operation, decreases the focal distance and increases the eyesight angle to the surface. This allows view an smaller
463
         * area but with the items bigger.</p>
464
         */
465
        public static double ZOOMINFACTOR=2;
466

    
467
        /**
468
         * <p>Default <i>zoom out</i> factor.</p>
469
         * <p>Doing a <i>zoom out</i> operation, increases the focal distance and decreases the eyesight angle to the surface. This allows view a bigger
470
         * area but with the items smaller.</p>
471
         */
472
        public static double ZOOMOUTFACTOR=0.5;
473

    
474
        /**
475
         *          * Draw version of the context. It's used for know when de componend has
476
         * changed any visualization property
477
         *
478
         *  @see getDrawVersion
479
         *  @see updateDrawVersion
480
         */
481
        private long drawVersion= 0L;
482

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

    
488
        /**
489
         * Object to Manage Draw of MapContext
490
         */
491
        private Class mapContextDrawerClass = null;
492

    
493
        /**
494
         * <p>Color used to represent the selections.</p>
495
         */
496
        private static Color selectionColor = Color.YELLOW;
497
        private ArrayList layersToSnap = new ArrayList();
498

    
499

    
500
        /**
501
         * <p>Gets the color used to represent the selections.</p>
502
         *
503
         * @return color used to represent the selections
504
         */
505
        public static Color getSelectionColor() {
506
                return selectionColor;
507
        }
508

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

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

    
528
        public MapContext() { }
529

    
530
        /**
531
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
532
         *
533
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
534
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
535
         */
536
        public MapContext(FLayers fLayers, ViewPort vp) {
537
                this.layers = fLayers;
538
                
539
                layerEventListener = new LayerEventListener();
540

    
541
                if (layers != null) {
542
                        layers.setMapContext(this);
543
                        layers.addLayerCollectionListener(layerEventListener);
544
                        layers.addLayerCollectionListener(eventBuffer);
545
                }
546

    
547
                setViewPort(vp);
548
        }
549

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

    
567
        /**
568
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
569
         *
570
         * @param listener the legend listener
571
         *
572
         * @see #removeLayerListener(LegendListener)
573
         * @see #callLegendChanged()
574
         */
575
        public void addLayerListener(LegendListener listener) {
576
                if (!legendListeners.contains(listener)){
577
                        legendListeners.add(listener);
578
                }
579
        }
580
        // SUGERENCIA DE PABLO
581
        //        public void addLegendListener(LegendListener listener) {
582
        //                if (!legendListeners.contains(listener))
583
        //                        legendListeners.add(listener);
584
        //        }
585

    
586
        /**
587
         * <p>Adds the specified layer drawing listener to catch and handle drawing events from layers of this map.</p>
588
         *
589
         * @param listener the listener to add
590
         *
591
         * @see #removeLayerDrawListener(LayerDrawingListener)
592
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
593
         */
594
        public void addLayerDrawingListener(LayerDrawingListener listener) {
595
                layerDrawingListeners.add(listener);
596
        }
597

    
598
        /**
599
         * <p>Removes the specified layer drawing listener from this map.</p>
600
         *
601
         * @param listener the listener to remove
602
         *
603
         * @see #addLayerDrawingListener(LayerDrawingListener)
604
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
605
         */
606
        public void removeLayerDrawListener(LayerDrawingListener listener) {
607
                layerDrawingListeners.remove(listener);
608
        }
609

    
610
        /**
611
         * <p>Adds the specified error listener to receive error events from this map.</p>
612
         *
613
         * @param listener the listener to add
614
         *
615
         * @see #removeErrorListener(LegendListener)
616
         * @see #reportDriverExceptions(String, List)
617
         */
618
        public void addErrorListener(ErrorListener listener) {
619
                errorListeners.add(listener);
620
        }
621

    
622
        /**
623
         * <p>Removes the specified error listener from this map.</p>
624
         *
625
         * @param listener the listener to remove
626
         *
627
         * @see #addErrorListener(ErrorListener)
628
         * @see #reportDriverExceptions(String, List)
629
         */
630
        public void removeErrorListener(LegendListener listener) {
631
                legendListeners.remove(listener);
632
        }
633

    
634
        // SUGERENCIA DE PABLO:
635
        //public void removeErrorListener(ErrorListener listener) {
636
        //        errorListeners.remove(listener);
637
        //}
638

    
639
        /**
640
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
641
         * <p>This method must be called only if it's wanted to reflect a legend change.</p>
642
         *
643
         * @see #addLayerListener(LegendListener)
644
         * @see #removeLayerListener(LegendListener)
645
         */
646
        public synchronized void callLegendChanged() {
647
                for (int i = 0; i < legendListeners.size(); i++) {
648
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
649
                }
650
                // getLayers().moveTo(0,0);
651
        }
652

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

    
685
        /**
686
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
687
         *
688
         * @param e the event with information of the error
689
         *
690
         * @see #addErrorListener(ErrorListener)
691
         * @see #removeErrorListener(LegendListener)
692
         * @see #reportDriverExceptions(String, List)
693
         */
694
        public synchronized void callNewErrorEvent(ErrorEvent e) {
695
                for (int i = 0; i < errorListeners.size(); i++) {
696
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
697
                }
698
                errorListeners.clear();
699
                // getLayers().moveTo(0,0);
700
        }
701

    
702
        /**
703
         * <p>Removes the specified layer listener from this map.</p>
704
         *
705
         * @param listener the listener to remove
706
         *
707
         * @see #addLayerListener(LegendListener)
708
         * @see #callLegendChanged()
709
         */
710
        public void removeLayerListener(LegendListener listener) {
711
                legendListeners.remove(listener);
712
        }
713

    
714
        // SUGERENCIA DE PABLO:
715
        // public void removeLegendListener(LegendListener listener) {
716
        //         legendListeners.remove(listener);
717
        // }
718

    
719
        /**
720
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
721
         *
722
         * @return the hierarchy of nodes of layers stored in this map
723
         */
724
        public FLayers getLayers() {
725
                return layers;
726
        }
727

    
728
        /**
729
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
730
         *
731
         * @param b image with an accessible buffer of image data
732
         */
733
        public void drawLabels(BufferedImage b) {
734
        }
735

    
736
        /**
737
         * @see #redraw()
738
         */
739
        public void invalidate() {
740
                if (getLayers().getLayersCount() > 0) {
741
                        getLayers().moveTo(0, 0);
742
                }
743
        }
744

    
745
    /**
746
     * <p>
747
     * Prints the layers of this map using the {@link Graphics2D Graphics2D}
748
     * argument, that usually is the {@link Graphics Graphics} of the printer.
749
     * </p>
750
     * 
751
     * @param g
752
     *            for rendering 2-dimensional shapes, text and images on the
753
     *            Java(tm) platform
754
     * @param scale
755
     *            the scale of the view. Must be between
756
     *            {@linkplain FLayer#getMinScale()} and
757
     *            {@linkplain FLayer#getMaxScale()}.
758
     * @param properties
759
     *            a set with the settings to be applied to a whole print job and
760
     *            to all the documents in the print job
761
     * @throws MapContextException
762
     *             if there is an error getting the instance of MapContextDrawer
763
     * 
764
     * @throws ReadDriverException
765
     *             if fails reading with driver.
766
     * 
767
     * @see FLayers#print(Graphics2D, ViewPort, Cancellable, double,
768
     *      PrintAttributes)
769
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
770
     *      double)
771
     */
772
        public void print(Graphics2D g, double scale,
773
                        PrintAttributes properties) throws ReadException,
774
            MapContextException {
775
                
776
                CompatLocator.getGraphicsUtils().setRenderingHintsForPrinting(g);
777

    
778
                Cancellable cancel = new Cancellable() {
779
                        public boolean isCanceled() {
780
                                return false;
781
                        }
782

    
783
                        public void setCanceled(boolean canceled) {
784
                                // No queremos que se pueda cancelar la impresi?n.
785

    
786
                        }
787
                };
788
                this.getMapContextDrawer().print(this.layers, g, cancel, scale,properties);
789
                if (tracLayer != null) {
790
                        tracLayer.draw(null, g, viewPort, cancel, scale);
791
                }
792
        }
793

    
794
        /**
795
         * <p>Returns a new <code>MapContext</code> instance with the information of the <code>vp</code> argument, and the layers of this map.</p>
796
         *
797
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
798
         *
799
         * @return a new <code>MapContext</code> instance projected by <code>vp</code>
800
         */
801
        public MapContext createNewFMap(ViewPort vp) {
802
                MapContext ret = new MapContext(vp);
803
                ret.layers = this.layers;
804

    
805
                return ret;
806
        }
807

    
808
        /**
809
         * <p>Creates a new independent <code>MapContext</code> instance, that has a clone of the layers and the view port of this one.</p>
810
         * <p>The new map will have the same data source drivers to avoid waste memory, and work faster.</p>
811
         *
812
         * @return the new <code>MapContext</code> instance
813
         *
814
         * @throws XMLException if fails cloning the view port or a layer
815
         *
816
         * @see FLayer#cloneLayer()
817
         * @see ViewPort#cloneViewPort()
818
         */
819
        public MapContext cloneFMap() {
820
                ViewPort vp = getViewPort().cloneViewPort();
821
                FLayers antLayers = getLayers();
822
                MapContext ret = new MapContext(vp);
823
                FLayers aux = new FLayers();//(ret,null);
824
                aux.setMapContext(ret);
825
                for (int i=0; i < antLayers.getLayersCount(); i++)
826
                {
827
                        FLayer lyr = antLayers.getLayer(i);
828
                        try {
829
                                aux.addLayer(lyr.cloneLayer());
830
                        } catch (Exception e) {
831
                                throw new RuntimeException(e);
832
                        }
833
                }
834
                ret.layers = aux;
835
                return ret;
836

    
837

    
838
        }
839

    
840
        /**
841
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
842
         *
843
         * @return the new map
844
         */
845
        public MapContext cloneToDraw() {
846
                ViewPort vp = getViewPort().cloneViewPort();
847
                MapContext mapContext=new MapContext(getLayers(),vp);
848
                return mapContext;
849
        }
850

    
851

    
852
        /**
853
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
854
         *
855
         * @param vectorial the layer to add
856
         */
857
        public void addToTrackLayer(FLayer vectorial) {
858
        }
859

    
860
        /**
861
         * <p>Returns the scale of the view in the screen.</p>
862
         *
863
         * @return one of this values:
864
         * <ul>
865
         * <li>the scale of the adjusted extent scale of the view in the screen
866
         * <li><code>-1</code> if there is no image
867
         * <li><code>0</code> if there is no extent defined for the image
868
         * </ul>
869
         *
870
         * @see #setScaleView(long)
871
         * @see ViewPort#getAdjustedExtent()
872
         * @see IProjection#getScale(double, double, double, double)
873
         */
874
        public long getScaleView() {
875
                double dpi = getScreenDPI();
876
                IProjection proj = viewPort.getProjection();
877

    
878
                if (viewPort.getImageSize() == null) {
879
                        return -1;
880
                }
881

    
882
                if (viewPort.getAdjustedExtent() == null) {
883
                        return 0;
884
                }
885
                double[] trans2Meter=getDistanceTrans2Meter();
886
                if (proj == null) {
887
                        double w = ((viewPort.getImageSize().width / dpi) * 2.54);
888
                        return (long) (viewPort.getAdjustedExtent().getLength(0) / w * trans2Meter[getViewPort()
889
                                        .getMapUnits()]);
890
                }
891

    
892
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinimum(0)*trans2Meter[getViewPort().getMapUnits()]),
893
                                (viewPort.getAdjustedExtent().getMaximum(0)*trans2Meter[getViewPort().getMapUnits()]),
894
                                viewPort.getImageSize().width, dpi));
895

    
896
        }
897

    
898
        /**
899
         * <p>Sets the new extent of the view, calculated using the scale argument.</p>
900
         * <p>Doesn't updates the scale if there isn't information about the dimension of the image or the
901
         *  adjusted extent.</p>
902
         *
903
         * @param scale the new scale for the view
904
         *
905
         * @see ViewPort#setProjection(IProjection)
906
         * @see #getScaleView()
907
         */
908
        public void setScaleView(long scale) {
909
                double dpi = getScreenDPI();
910
                if (viewPort.getImageSize() == null) {
911
                        return;
912
                }
913
                IProjection proj = viewPort.getProjection();
914
                if (viewPort.getAdjustedExtent() == null) {
915
                        return;
916
                }
917
                double[] trans2Meter=getDistanceTrans2Meter();
918
                Envelope env=viewPort.getAdjustedExtent();
919
                Rectangle2D r=new Rectangle2D.Double(env.getMinimum(0),env.getMinimum(1),env.getLength(0),env.getLength(1));
920
                Rectangle2D rec=proj.getExtent(r,scale,viewPort.getImageWidth(),viewPort.getImageHeight(),100*getDistanceTrans2Meter()[getViewPort().getMapUnits()],trans2Meter[getViewPort().getDistanceUnits()],dpi);
921
                try {
922
                        getViewPort().setEnvelope(geomManager.createEnvelope(rec.getX(),rec.getY(),rec.getMaxX(),rec.getMaxY(), SUBTYPES.GEOM2D));
923
                } catch (CreateEnvelopeException e) {
924
                        logger.error("Error seting the bounding box");
925
                }
926
        }
927

    
928
        /**
929
         * <p>Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
930
         * by default as it is defined in the default Toolkit.</p>
931
         *
932
         * @return double with the screen's dpi
933
         */
934
        public static double getScreenDPI() {
935
                return CompatLocator.getGraphicsUtils().getScreenDPI();
936
        }
937

    
938
        /**
939
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
940
         */
941
//        public void setVectorial(VectorialAdapter v) {
942
//        }
943

    
944
        /**
945
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
946
         */
947
        public void process(Visitor visitor) {
948
        }
949

    
950
        /**
951
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
952
         */
953
        public void processSelected(Visitor visitor) {
954
        }
955

    
956
        /**
957
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
958
         *      VectorialSubSet)
959
         */
960
        public void select(Visitor visitor) {
961
        }
962

    
963
        /**
964
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
965
         */
966
        public void selectFromSelection() {
967
        }
968

    
969
        /**
970
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
971
         */
972
        public void createIndex() {
973
        }
974

    
975
        /**
976
         * @see org.cresques.geo.Projected#getProjection()
977
         *
978
         * @see ViewPort#getProjection()
979
         * @see #setProjection(IProjection)
980
         * @see #reProject(ICoordTrans)
981
         */
982
        public IProjection getProjection() {
983
                return getViewPort().getProjection();
984
        }
985

    
986
        /**
987
         * <p>Sets the new projection.</p>
988
         *
989
         * @param proj the new projection
990
         *
991
         * @see #getProjection()
992
         * @see ViewPort#setProjection(IProjection)
993
         * @see #reProject(ICoordTrans)
994
         */
995
        public void setProjection(IProjection proj) {
996
                if (getViewPort() != null) {
997
                        getViewPort().setProjection(proj);
998
                }
999
        }
1000

    
1001
        /**
1002
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
1003
         */
1004
        public void reProject(ICoordTrans arg0) {
1005
                // TODO implementar reprojecci?n (lo que sea eso)
1006
        }
1007

    
1008
        /**
1009
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#getSelectionBounds()
1010
         *
1011
         * @see SelectedZoomVisitor#getSelectBound()
1012
         */
1013
        public Envelope getSelectionBounds() throws BaseException {
1014
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
1015

    
1016
                layers.accept(visitor);
1017

    
1018
                return visitor.getSelectBound();
1019
        }
1020

    
1021
    /**
1022
     * <p>
1023
     * Draws this map if its {@link ViewPort ViewPort} has an extent defined:<br>
1024
     * <ol>
1025
     * <li>Selects only the layers that have to be drawn:
1026
     * {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
1027
     * <li>Sets quality: antialiasing by text and images, and quality rendering.
1028
     * <li>Draws the layers.
1029
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
1030
     * <li>Draws the graphic layer.
1031
     * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
1032
     * <li>Invokes the garbage collector and memory clean.
1033
     * </ol>
1034
     * </p>
1035
     * 
1036
     * @param image
1037
     *            buffer used sometimes instead <code>g</code> to accelerate the
1038
     *            draw. For example, if two points are as closed that can't be
1039
     *            distinguished, draws only one.
1040
     * @param g
1041
     *            for rendering 2-dimensional shapes, text and images on the
1042
     *            Java(tm) platform
1043
     * @param cancel
1044
     *            shared object that determines if this layer can continue being
1045
     *            drawn
1046
     * @param scale
1047
     *            the scale of the view. Must be between
1048
     *            {@linkplain FLayer#getMinScale()} and
1049
     *            {@linkplain FLayer#getMaxScale()}.
1050
     * @throws MapContextException
1051
     *             if there is an error getting the instance of MapContextDrawer
1052
     * @throws ReadDriverException
1053
     *             if fails reading with the driver.
1054
     */
1055
        public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
1056
                        double scale) throws ReadException, MapContextException {
1057
                if (viewPort.getExtent() == null) {
1058
                        return;
1059
                }
1060

    
1061
                CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1062

    
1063
                long t1 = System.currentTimeMillis();
1064

    
1065
                this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1066

    
1067
                if (tracLayer != null) {
1068
                        LayerDrawEvent beforeTracLayerEvent =
1069
                                        new LayerDrawEvent(tracLayer, g, viewPort,
1070
                                                        LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1071
                        fireLayerDrawingEvent(beforeTracLayerEvent);
1072
                        tracLayer.draw(image, g, viewPort, cancel, scale);
1073
                        LayerDrawEvent afterTracLayerEvent =
1074
                                        new LayerDrawEvent(tracLayer, g, viewPort,
1075
                                                        LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1076
                        fireLayerDrawingEvent(afterTracLayerEvent);
1077
                }
1078

    
1079
                long t2 = System.currentTimeMillis();
1080
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
1081
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
1082
                System.gc();
1083
        }
1084

    
1085
        /**
1086
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1087
         *
1088
         * @param image image used to accelerate the screen draw
1089
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1090
         * @param cancel shared object that determines if this layer can continue being drawn
1091
         * @param scale value that represents the scale
1092
         * @throws ReadDriverException if fails reading with the driver.
1093
         *
1094
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1095
         */
1096
        public void drawGraphics(BufferedImage image, Graphics2D g,
1097
                        Cancellable cancel, double scale) throws ReadException {
1098
                if (viewPort == null) {
1099
                        return;
1100
                }
1101
                if (tracLayer != null) {
1102
                        tracLayer.draw(image, g, viewPort, cancel, scale);
1103
                }
1104
        }
1105

    
1106
    /**
1107
     * <p>
1108
     * Like
1109
     * {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}
1110
     * , but creating the task as cancellable.
1111
     * </p>
1112
     * 
1113
     * @param image
1114
     *            buffer used sometimes instead <code>g</code> to accelerate the
1115
     *            draw. For example, if two points are as closed that can't be
1116
     *            distinguished, draws only one.
1117
     * @param g
1118
     *            for rendering 2-dimensional shapes, text and images on the
1119
     *            Java(tm) platform
1120
     * @param scale
1121
     *            the scale of the view. Must be between
1122
     *            {@linkplain FLayer#getMinScale()} and
1123
     *            {@linkplain FLayer#getMaxScale()}.
1124
     * @throws MapContextException
1125
     *             if there is an error getting the instance of MapContextDrawer
1126
     * 
1127
     * @throws ReadDriverException
1128
     *             if the driver fails reading.
1129
     * 
1130
     * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1131
     */
1132
        public void draw(BufferedImage image, Graphics2D g, double scale)
1133
                        throws ReadException, MapContextException {
1134
                draw(image, g, new Cancellable() {
1135
                        /**
1136
                         * @see org.gvsig.utils.swing.threads.Cancellable#isCanceled()
1137
                         */
1138
                        public boolean isCanceled() {
1139
                                return false;
1140
                        }
1141

    
1142
                        public void setCanceled(boolean canceled) {
1143
                                // Do nothing
1144
                        }
1145
                }, scale);
1146
        }
1147

    
1148
        /**
1149
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1150
         *
1151
         * @return the view port
1152
         *
1153
         * @see #setViewPort(ViewPort)
1154
         */
1155
        public ViewPort getViewPort() {
1156
                return viewPort;
1157
        }
1158

    
1159
        /**
1160
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1161
         *  of this map.</p>
1162
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1163
         *  adds the new one.</p>
1164
         *
1165
         * @param viewPort the viewPort
1166
         *
1167
         * @see #getViewPort()
1168
         */
1169
        public void setViewPort(ViewPort viewPort) {
1170
                if (this.viewPort != null) {
1171
                        this.viewPort.removeViewPortListener(eventBuffer);
1172
                }
1173

    
1174
                if (this.mapContextDrawer != null){
1175
                        this.mapContextDrawer.setViewPort(viewPort);
1176
                }
1177

    
1178
                this.viewPort = viewPort;
1179
                if (viewPort != null) {
1180
                        viewPort.addViewPortListener(eventBuffer);
1181
                }
1182
        }
1183

    
1184
        /**
1185
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1186
         *
1187
         * @param extent the extent of the new zoom
1188
         */
1189
        public void zoomToEnvelope(Envelope extent) {
1190
                if (extent!=null) {
1191
                        getViewPort().setEnvelope(extent);
1192
                }
1193
        }
1194

    
1195
        /**
1196
         * <p>Returns the union of all extents of all layers of this map.</p>
1197
         *
1198
         * @return full extent of layers of this map
1199
         * @throws ReadDriverException if the driver fails reading.
1200
         *
1201
         * @see FLayers#getFullEnvelope()
1202
         */
1203
        public Envelope getFullEnvelope() throws ReadException {
1204
                return layers.getFullEnvelope();
1205
        }
1206

    
1207

    
1208

    
1209
        /**
1210
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1211
         *
1212
         * @param listener the new listener
1213
         *
1214
         * @return <code>true</code> if has added the listener successfully
1215
         *
1216
         * @see #removeAtomicEventListener(AtomicEventListener)
1217
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1218
         */
1219
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1220
                return eventBuffer.addAtomicEventListener(listener);
1221
        }
1222

    
1223
        /**
1224
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1225
         *
1226
         * @param listener the listener to remove
1227
         *
1228
     * @return <tt>true</tt> if the list contained the specified element
1229
         *
1230
         * @see #addAtomicEventListener(AtomicEventListener)
1231
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1232
         */
1233
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1234
                return eventBuffer.removeAtomicEventListener(listener);
1235
        }
1236

    
1237
        /**
1238
         * @see EventBuffer#beginAtomicEvent()
1239
         *
1240
         * @see #endAtomicEvent()
1241
         */
1242
        public void beginAtomicEvent() {
1243
                eventBuffer.beginAtomicEvent();
1244
        }
1245

    
1246
        /**
1247
         * @see EventBuffer#endAtomicEvent()
1248
         *
1249
         * @see #beginAtomicEvent()
1250
         */
1251
        public void endAtomicEvent() {
1252
                eventBuffer.endAtomicEvent();
1253
        }
1254

    
1255
        /**
1256
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1257
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1258
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1259
         *
1260
         * @author Fernando Gonz?lez Cort?s
1261
         */
1262
        public class LayerEventListener implements LayerCollectionListener {
1263
                /*
1264
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1265
                 */
1266
                public void layerAdded(LayerCollectionEvent e) {
1267
                        // Si es la primera capa, fijamos su extent al ViewPort
1268
                        // if (getLayers().getLayersCount() == 1) {
1269
                        if (getViewPort().getExtent() == null) {
1270
                                FLayer lyr = e.getAffectedLayer();
1271
                                if (lyr.isAvailable()) {
1272
                                        try {
1273
                                                getViewPort().setEnvelope(lyr.getFullEnvelope());
1274
                                        } catch (ReadException e1) {
1275
                                                e1.printStackTrace();
1276
                                        }
1277
                                }
1278
                        }
1279

    
1280
                        // Registramos al FMap como listener del legend de las capas
1281
                        FLayer lyr = e.getAffectedLayer();
1282
                        selectionListener(lyr);
1283
                        if (lyr instanceof SingleLayer){
1284
                                if (((SingleLayer) lyr).getDataStore() != null) {
1285
                                        ((SingleLayer) lyr).getDataStore().addObserver(
1286
                                                        MapContext.this);
1287
                                }
1288
                        }
1289
                }
1290

    
1291
                /**
1292
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1293
                 *
1294
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1295
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1296
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1297
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1298
                 *
1299
                 * @param the layer or layers
1300
                 */
1301
                private void selectionListener(FLayer lyr){
1302
                        lyr.addLayerListener(eventBuffer);
1303

    
1304
                        if (lyr instanceof Classifiable) {
1305
                                Classifiable c = (Classifiable) lyr;
1306
                                c.addLegendListener(eventBuffer);
1307
                        }
1308

    
1309
                        if (lyr instanceof FLayers){
1310
                                FLayers lyrs=(FLayers)lyr;
1311
                                for(int i=0;i<lyrs.getLayersCount();i++){
1312
                                        selectionListener(lyrs.getLayer(i));
1313
                                }
1314
                        }
1315

    
1316
                }
1317
                /*
1318
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1319
                 */
1320
                public void layerMoved(LayerPositionEvent e) {
1321
                }
1322

    
1323
                /*
1324
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1325
                 */
1326
                public void layerRemoved(LayerCollectionEvent e) {
1327
                        FLayer lyr = e.getAffectedLayer();
1328

    
1329
                        lyr.removeLayerListener(eventBuffer);
1330

    
1331
                        if (lyr instanceof Classifiable) {
1332
                                Classifiable c = (Classifiable) lyr;
1333
                                c.removeLegendListener(eventBuffer);
1334
                        }
1335

    
1336
                        if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore()!=null) {
1337
                                ((SingleLayer) lyr).getDataStore().deleteObserver(
1338
                                                MapContext.this);
1339
                        }
1340

    
1341
                }
1342

    
1343
                /*
1344
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1345
                 */
1346
                public void layerAdding(LayerCollectionEvent e)
1347
                                throws CancelationException {
1348
                }
1349

    
1350
                /*
1351
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1352
                 */
1353
                public void layerMoving(LayerPositionEvent e)
1354
                                throws CancelationException {
1355
                }
1356

    
1357
                /*
1358
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1359
                 */
1360
                public void layerRemoving(LayerCollectionEvent e)
1361
                                throws CancelationException {
1362
                }
1363

    
1364

    
1365
                /*
1366
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1367
                 */
1368
                public void visibilityChanged(LayerCollectionEvent e)
1369
                                throws CancelationException {
1370
                }
1371
        }
1372

    
1373
        /**
1374
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1375
         *  collection of layers argument.</p>
1376
         *
1377
         * @param a collection of layers
1378
         */
1379
        public void addAsCollectionListener(FLayers layers2) {
1380
                layers2.addLayerCollectionListener(layerEventListener);
1381
        }
1382

    
1383
        /**
1384
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1385
         *
1386
         * @return the graphic layer of this map
1387
         *
1388
         * @see #setGraphicsLayer(GraphicLayer)
1389
         */
1390
        public GraphicLayer getGraphicsLayer() {
1391
                if (tracLayer == null) {
1392
                        if (getViewPort() != null) {
1393
                                this.tracLayer =
1394
                                                MapContextLocator.getMapContextManager()
1395
                                                                .createGraphicsLayer(
1396
                                                                                getViewPort().getProjection());
1397
                        } else {
1398
                                this.tracLayer =
1399
                                                MapContextLocator.getMapContextManager()
1400
                                                                .createGraphicsLayer(null);
1401
                        }
1402
                }
1403
                return tracLayer;
1404
        }
1405

    
1406
        /**
1407
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1408
         *
1409
         * @param graphicLayer the new graphic layer
1410
         *
1411
         * @see #getGraphicsLayer()
1412
         */
1413
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1414
                tracLayer = graphicLayer;
1415
        }
1416

    
1417
        /**
1418
         * <p>Indicates whether some other object is "equal to" this map.</p>
1419
         * <p>Returns <code>true</code> if success one of this options:
1420
         * <ul>
1421
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1422
         * <li>Both maps have the same layers.
1423
         * <li>Both maps have the same number of layers and with the same name.
1424
         * </ul>
1425
         * </p>
1426
         *
1427
         * @param obj the reference object with which to compare.
1428
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1429
         *
1430
         * @see Object#equals(Object)
1431
         */
1432
        public boolean equals(Object arg0) {
1433
                if (!(arg0 instanceof MapContext)) {
1434
                        return false;
1435
                }
1436
                MapContext map = (MapContext) arg0;
1437
                if (super.equals(arg0)) {
1438
                        return true;
1439
                }
1440
                if (getLayers() == map.getLayers()) {
1441
                        return true;
1442
                }
1443
                boolean isEqual = true;
1444
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1445
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1446

    
1447
                                if (!getLayers().getLayer(i).getName().equals(
1448
                                                map.getLayers().getLayer(i).getName())) {
1449
                                        isEqual = false;
1450
                                }
1451

    
1452
                        }
1453
                } else {
1454
                        isEqual = false;
1455
                }
1456
                return isEqual;
1457
        }
1458

    
1459
        /**
1460
         * <p>Registers the message of an error associated to this map.</p>
1461
         *
1462
         * @param stringProperty the error message
1463
         *
1464
         * @see #getLayersError()
1465
         * @see #clearErrors()
1466
         */
1467
        public void addLayerError(String stringProperty) {
1468
                layersError.add(stringProperty);
1469
        }
1470

    
1471
        /**
1472
         * <p>Gets the list with all error messages registered to this map.</p>
1473
         *
1474
         * @return the list of errors registered to this map
1475
         *
1476
         * @see #addLayerError(String)
1477
         * @see #clearErrors()
1478
         */
1479
        public ArrayList getLayersError() {
1480
                return layersError;
1481
        }
1482

    
1483
        /**
1484
         * <p>Removes all error messages associated to this map.</p>
1485
         *
1486
         * @see #addLayerError(String)
1487
         * @see #getLayersError()
1488
         */
1489
        public void clearErrors() {
1490
                layersError.clear();
1491
        }
1492

    
1493
        /**
1494
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1495
         *
1496
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1497
         * @return the new layer node
1498
         */
1499
        public FLayers getNewGroupLayer(FLayers parent) {
1500
                FLayers group1 = new FLayers();//(this,parent);
1501
                group1.setMapContext(this);
1502
                group1.setParentLayer(parent);
1503
            return group1;
1504
        }
1505

    
1506
        public String getClassName() {
1507
                return null;
1508
        }
1509

    
1510
        public ArrayList getLayersToSnap() {
1511
                return layersToSnap;
1512
        }
1513

    
1514
        public void setLayersToSnap(ArrayList layersToSnap) {
1515
                this.layersToSnap = layersToSnap;
1516

    
1517
        }
1518

    
1519
        public void update(Observable observable, Object notification) {
1520
                // TODO REVISAR ESTO!!!
1521
                String ntype=null;
1522
                if (notification instanceof FeatureStoreNotification) {
1523
                        FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1524
                        ntype =fsNotification.getType();
1525
                        if (
1526
                                        ntype.equals(FeatureStoreNotification.LOAD_FINISHED)||
1527
                                        ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)
1528
                        ) {
1529
                                getLayers().moveTo(0, 0);
1530
                        }
1531
                }
1532
        }
1533

    
1534
        public long getDrawVersion() {
1535
                return this.drawVersion;
1536
        }
1537

    
1538
        protected void updateDrawVersion(){
1539
                this.drawVersion++;
1540
        }
1541

    
1542
        public MapContextDrawer getMapContextDrawer() throws ReadException,
1543
            MapContextException {
1544
                if (this.mapContextDrawer == null){
1545
                    if (mapContextDrawerClass == null) {
1546
                this.mapContextDrawer = mapContextManager
1547
                        .createDefaultMapContextDrawerInstance();
1548
            } else {
1549
                this.mapContextDrawer = mapContextManager
1550
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1551
            }
1552
                    this.mapContextDrawer.setMapContext(this);
1553
            this.mapContextDrawer.setViewPort(viewPort);
1554
                }
1555

    
1556
                return this.mapContextDrawer;
1557
        }
1558
        
1559
        public void setMapContextDrawerClass(Class mapContextDrawerClass)
1560
            throws MapContextException {
1561
                mapContextManager.validateMapContextDrawer(mapContextDrawerClass);
1562
                this.mapContextDrawerClass = mapContextDrawerClass;
1563
                if (this.mapContextDrawer != null){
1564
                        this.mapContextDrawer.dispose();
1565
                        this.mapContextDrawer = null;
1566
                }
1567
        }
1568

    
1569
        public void setMapContextDrawer(MapContextDrawer drawer){
1570
                if (this.mapContextDrawer != null){
1571
                        this.mapContextDrawer.dispose();
1572
                        this.mapContextDrawer = null;
1573
                }
1574
                this.mapContextDrawer = drawer;
1575
                if (this.mapContextDrawer != null){
1576
                        this.mapContextDrawer.setMapContext(this);
1577
                        this.mapContextDrawer.setViewPort(viewPort);
1578
                }
1579
        }
1580
        
1581
        public void loadFromState(PersistentState state)
1582
                        throws PersistenceException {
1583
                
1584
                ViewPort vp = (ViewPort) state.get("ViewPort");
1585
                setViewPort(vp);
1586
                
1587
                layers = (FLayers) state.get("layers");
1588
                layers.setName("root layer");
1589
                loadLayers(layers);
1590
                layers.setMapContext(this);
1591
                
1592
                layerEventListener = new LayerEventListener();
1593
                layers.addLayerCollectionListener(layerEventListener);
1594
                
1595
                layers.addLayerCollectionListener(eventBuffer);
1596
                layers.setProjection(vp.getProjection());
1597
                
1598
                
1599
        }
1600

    
1601
        private void loadLayers(FLayers lyrs) {
1602
                
1603
                int sz = lyrs.getLayersCount();
1604
                for (int i=0; i<sz; i++) {
1605
                        try {
1606
                                lyrs.getLayer(i).load();
1607
                        } catch (LoadLayerException e) {
1608
                                logger.error("While loading layer: " + lyrs.getLayer(i).getName());
1609
                        }
1610
                }
1611
        }
1612

    
1613
        public void saveToState(PersistentState state) throws PersistenceException {
1614
                state.set("ViewPort", viewPort);
1615
                state.set("layers", layers);
1616
        }
1617

    
1618
        public static void registerPersistent() {
1619
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
1620
                DynStruct definition = manager.addDefinition(
1621
                                MapContext.class,
1622
                                "MapContext",
1623
                                "MapContext Persistence definition",
1624
                                null, 
1625
                                null
1626
                );
1627
                definition.addDynFieldObject("ViewPort")
1628
                        .setClassOfValue(ViewPort.class)
1629
                        .setMandatory(true);
1630
        
1631
                definition.addDynFieldObject("layers")
1632
                        .setClassOfValue(FLayers.class)
1633
                        .setMandatory(true);
1634
        }
1635

    
1636
        protected void doDispose() throws BaseException {
1637
                dispose(layers);
1638
                dispose(tracLayer);
1639
        }
1640
}