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

History | View | Annotate | Download (53.5 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
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.mapcontext;
25

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

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

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

    
81

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

    
140

    
141
        public static ArrayList AREANAMES=new ArrayList();
142
        public static ArrayList AREAABBR=new ArrayList();
143
        public static ArrayList AREATRANS2METER=new ArrayList();
144

    
145
        public static ArrayList DISTANCENAMES=new ArrayList();
146
        public static ArrayList DISTANCEABBR=new ArrayList();
147
        public static ArrayList DISTANCETRANS2METER=new ArrayList();
148

    
149
        static{
150
                MapContext.addDistanceUnit("Kilometros","Km",1000);
151
            MapContext.addDistanceUnit("Metros","m",1);
152
            MapContext.addDistanceUnit("Centimetros","cm",0.01);
153
            MapContext.addDistanceUnit("Milimetros","mm",0.001);
154
            MapContext.addDistanceUnit("Millas","mi",1609.344);
155
            MapContext.addDistanceUnit("Yardas","Ya",0.9144);
156
            MapContext.addDistanceUnit("Pies","ft",0.3048);
157
            MapContext.addDistanceUnit("Pulgadas","inche",0.0254);
158
            MapContext.addDistanceUnit("Grados","?",1/8.983152841195214E-6);
159

    
160
            MapContext.addAreaUnit("Kilometros","Km",true,1000);
161
            MapContext.addAreaUnit("Metros","m",true,1);
162
            MapContext.addAreaUnit("Centimetros","cm",true,0.01);
163
            MapContext.addAreaUnit("Milimetros","mm",true,0.001);
164
            MapContext.addAreaUnit("Millas","mi",true,1609.344);
165
            MapContext.addAreaUnit("Yardas","Ya",true,0.9144);
166
            MapContext.addAreaUnit("Pies","ft",true,0.3048);
167
            MapContext.addAreaUnit("Pulgadas","inche",true,0.0254);
168
            MapContext.addAreaUnit("Grados","?",true,1/8.983152841195214E-6);
169

    
170

    
171
    }
172
        
173
        private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
174
        
175
        private static final MapContextManager mapContextManager = MapContextLocator
176
            .getMapContextManager(); 
177
        
178
        private static final Logger logger = LoggerFactory.getLogger(MapContext.class);
179

    
180
        /**
181
         * <p>Determines the number of frames.</p>
182
         *
183
         * <p>Number of updates per second that the timer will invoke repaint this component.</p>
184
         */
185
    private static int drawFrameRate = 3;
186
    /**
187
         * <p>Returns the draw frame rate.</p>
188
         *
189
         * <p>Draw frame rate is the number of repaints of this <code>MapControl</code> instance that timer invokes per second.</p>
190
         *
191
         * @return number of repaints of this <code>MapControl</code> instance that timer invokes per second
192
         *
193
         * @see #applyFrameRate()
194
         * @see #setDrawFrameRate(int)
195
         */
196
        public static int getDrawFrameRate() {
197
                return drawFrameRate;
198
        }
199

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

    
279
        /**
280
         * <p>Defines the value which a unit of a distance measurement must be divided to obtain its equivalent <b>in centimeters</b>.</p>
281
         *
282
         * <p><b><i>Conversion values of distance measurements:</i></b>
283
         * <ul>
284
         *  <li><code>MapContext.CHANGE[0]</code>: kilometer
285
         *  <li><code>MapContext.CHANGE[1]</code>: meter
286
         *  <li><code>MapContext.CHANGE[2]</code>: centimeter
287
         *  <li><code>MapContext.CHANGE[3]</code>: millimeter
288
         *  <li><code>MapContext.CHANGE[4]</code>: international statute mile
289
         *  <li><code>MapContext.CHANGE[5]</code>: yard
290
         *  <li><code>MapContext.CHANGE[6]</code>: foot
291
         *  <li><code>MapContext.CHANGE[7]</code>: inch
292
         *  <li><code>MapContext.CHANGE[8]</code>: grade
293
         * </ul>
294
         *
295
         * <p><h3>Examples:</h3>
296
         * <pre>1 international statute mile / MapContext.CHANGE[4] = X centimeters</pre>
297
         * <pre>1 kilometer / MapContext.CHANGE[0] = X centimeters</pre>
298
         * <pre>1 grade / MapContext.CHANGE[8] = X centimeters</pre>
299
         * </p>
300
         *
301
         * <p><h3>Grade conversion value: <code>MapContext.CHANGE[8]</code></h3>
302
         * The value of <code>MapContext.CHANGE[8]</code> represents the centimeters of a straight line between two
303
         *  points on the Earth surface that are 1 grade far each other of the center of the Earth. This value has been calculated using
304
         *  a radius approximated of R<sub>Earth</sub>=6.37846082678100774672e6 meters, according these equations:
305
         * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
306
         * <pre>MapContext.CHANGE[8] = 1 / D</pre>
307
         * <h4>Explanation:</h4>
308
         * We get an isosceles triangle with the center of the Earth and the 2 points on the surface. This triangle can be divided into
309
         * 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
310
         * is the hypotenuse. Then we apply trigonometry and get the distance <i>D</i> between both points on the Earth surface.</p>
311
         * <p>Now we only must invert that value to obtain <code>MapContext.CHANGE[8]</code>.</p>
312
         * @deprecated use getDistanceTrans2Meter() * 100
313
         */
314
        public static final double[] CHANGE = { 100000, 100, 1, 0.1, 160934.4,
315
                        91.44, 30.48, 2.54, 1/8.983152841195214E-4 };
316

    
317
        /* Do not alter the order and the values of this array, if you need append values.*/
318
        /**
319
         * <p>Gets the name of all distance measurements supported by <code>MapContext</code>.</p>
320
         */
321
//        public static final String[] NAMES= {
322
//                Messages.getString("Kilometros"),
323
//                Messages.getString("Metros"),
324
//                Messages.getString("Centimetros"),
325
//                Messages.getString("Milimetros"),
326
//                Messages.getString("Millas"),
327
//                Messages.getString("Yardas"),
328
//                Messages.getString("Pies"),
329
//                Messages.getString("Pulgadas"),
330
//                Messages.getString("Grados"),
331
//        };
332

    
333
        public static final int EQUALS = 0;
334

    
335
        public static final int DISJOINT = 1;
336

    
337
        public static final int INTERSECTS = 2;
338

    
339
        public static final int TOUCHES = 3;
340

    
341
        public static final int CROSSES = 4;
342

    
343
        public static final int WITHIN = 5;
344

    
345
        public static final int CONTAINS = 6;
346

    
347
        public static final int OVERLAPS = 7;
348

    
349
        /**
350
         * A hierarchy of {@link FLayers FLayers} nodes.
351
         *
352
         * @see #getLayers()
353
         * @see #print(Graphics2D, double, PrintAttributes)
354
         */
355
        protected FLayers layers;
356

    
357
        /**
358
         * A layer with graphical items: geometries and symbols.
359
         *
360
         * @see #getGraphicsLayer()
361
         * @see #setGraphicsLayer(GraphicLayer)
362
         * @see #print(Graphics2D, double, PrintAttributes)
363
         */
364
        private GraphicLayer tracLayer = null;
365
                //MapContextLocator.getMapContextManager().createGraphicsLayer(getProjection());
366

    
367
        /**
368
         * Information for draw layers in a view.
369
         *
370
         * @see #getViewPort()
371
         * @see #setViewPort(ViewPort)
372
         */
373
        private ViewPort viewPort;
374

    
375
        // private ArrayList invalidationListeners = new ArrayList();
376

    
377
        /**
378
         * Array list with all {@link LegendListener LegendListener} registered to this map.
379
         *
380
         * @see #addLayerListener(LegendListener)
381
         * @see #removeLayerListener(LegendListener)
382
         * @see #callLegendChanged()
383
         */
384
        private ArrayList legendListeners = new ArrayList();
385

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

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

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

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

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

    
437

    
438

    
439
        // public static ResourceBundle myResourceBundle =
440
        // ResourceBundle.getBundle("FMap");
441

    
442
        /**
443
         * <p>Default <i>zoom in</i> factor.</p>
444
         * <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
445
         * area but with the items bigger.</p>
446
         */
447
        public static double ZOOMINFACTOR=2;
448

    
449
        /**
450
         * <p>Default <i>zoom out</i> factor.</p>
451
         * <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
452
         * area but with the items smaller.</p>
453
         */
454
        public static double ZOOMOUTFACTOR=0.5;
455

    
456
        /**
457
         *          * Draw version of the context. It's used for know when de componend has
458
         * changed any visualization property
459
         *
460
         *  @see getDrawVersion
461
         *  @see updateDrawVersion
462
         */
463
        private long drawVersion= 0L;
464
        
465
        private long layersVersion = 0L;
466
        private long viewPortVersion = 0L;
467
        private long graphicsLayerVersion = 0L;
468

    
469
        /**
470
         * Object to Manage Draw of MapContext
471
         */
472
        private MapContextDrawer mapContextDrawer= null;
473

    
474
        /**
475
         * Object to Manage Draw of MapContext
476
         */
477
        private Class mapContextDrawerClass = null;
478

    
479
        /**
480
         * <p>Color used to represent the selections.</p>
481
         */
482
        private static Color selectionColor = Color.YELLOW;
483
        private ArrayList layersToSnap = new ArrayList();
484

    
485

    
486
        /**
487
         * <p>Gets the color used to represent the selections.</p>
488
         *
489
         * @return color used to represent the selections
490
         */
491
        public static Color getSelectionColor() {
492
                return selectionColor;
493
        }
494

    
495
        /**
496
         * <p>Sets the color used to represent the selections.</p>
497
         *
498
         * @param selectionColor color used to represent the selections
499
         */
500
        public static void setSelectionColor(Color selectionColor) {
501
                MapContext.selectionColor = selectionColor;
502
        }
503

    
504
        /**
505
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
506
         *  without layers.</p>
507
         *
508
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
509
         */
510
        public MapContext(ViewPort vp) {
511
        this(new FLayers(), vp);
512
        }
513

    
514
        public MapContext() { }
515

    
516
        /**
517
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
518
         *
519
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
520
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
521
         */
522
        public MapContext(FLayers fLayers, ViewPort vp) {
523
                this.layers = fLayers;
524
                
525
                layerEventListener = new LayerEventListener();
526

    
527
                if (layers != null) {
528
                        layers.setMapContext(this);
529
                        layers.addLayerCollectionListener(layerEventListener);
530
                        layers.addLayerCollectionListener(eventBuffer);
531
                }
532

    
533
                setViewPort(vp);
534
        }
535

    
536
        /**
537
         * <p>Reports to all driver error listeners registered of a bundle of driver exceptions caused in the same map atomic transaction.</p>
538
         *
539
         * @param introductoryText introductory text specified by developer. If <code>null</code>, use ""
540
         * @param driverExceptions list with a bundle of driver exceptions caught during an atomic event
541
         *
542
         * @see #addErrorListener(ErrorListener)
543
         * @see #removeErrorListener(LegendListener)
544
         */
545
        public synchronized void reportDriverExceptions(String introductoryText,
546
                                                                                                        List driverExceptions){
547
                for (int i = 0; i < errorListeners.size(); i++) {
548
                        ((ErrorListener) errorListeners.get(i)).
549
                                reportDriverExceptions(introductoryText, driverExceptions);
550
                }
551
        }
552

    
553
        /**
554
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
555
         *
556
         * @param listener the legend listener
557
         *
558
         * @see #removeLayerListener(LegendListener)
559
         * @see #callLegendChanged()
560
         */
561
        public void addLayerListener(LegendListener listener) {
562
                if (!legendListeners.contains(listener)){
563
                        legendListeners.add(listener);
564
                }
565
        }
566
        // SUGERENCIA DE PABLO
567
        //        public void addLegendListener(LegendListener listener) {
568
        //                if (!legendListeners.contains(listener))
569
        //                        legendListeners.add(listener);
570
        //        }
571

    
572
        /**
573
         * <p>Adds the specified layer drawing listener to catch and handle drawing events from layers of this map.</p>
574
         *
575
         * @param listener the listener to add
576
         *
577
         * @see #removeLayerDrawListener(LayerDrawingListener)
578
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
579
         */
580
        public void addLayerDrawingListener(LayerDrawingListener listener) {
581
                layerDrawingListeners.add(listener);
582
        }
583

    
584
        /**
585
         * <p>Removes the specified layer drawing listener from this map.</p>
586
         *
587
         * @param listener the listener to remove
588
         *
589
         * @see #addLayerDrawingListener(LayerDrawingListener)
590
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
591
         */
592
        public void removeLayerDrawListener(LayerDrawingListener listener) {
593
                layerDrawingListeners.remove(listener);
594
        }
595

    
596
        /**
597
         * <p>Adds the specified error listener to receive error events from this map.</p>
598
         *
599
         * @param listener the listener to add
600
         *
601
         * @see #removeErrorListener(LegendListener)
602
         * @see #reportDriverExceptions(String, List)
603
         */
604
        public void addErrorListener(ErrorListener listener) {
605
                errorListeners.add(listener);
606
        }
607

    
608
        /**
609
         * <p>Removes the specified error listener from this map.</p>
610
         *
611
         * @param listener the listener to remove
612
         *
613
         * @see #addErrorListener(ErrorListener)
614
         * @see #reportDriverExceptions(String, List)
615
         */
616
        public void removeErrorListener(LegendListener listener) {
617
                legendListeners.remove(listener);
618
        }
619

    
620
        // SUGERENCIA DE PABLO:
621
        //public void removeErrorListener(ErrorListener listener) {
622
        //        errorListeners.remove(listener);
623
        //}
624

    
625
        /**
626
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
627
         * <p>This method must be called only if it's wanted to reflect a legend change.</p>
628
         *
629
         * @see #addLayerListener(LegendListener)
630
         * @see #removeLayerListener(LegendListener)
631
         */
632
        public synchronized void callLegendChanged() {
633
                for (int i = 0; i < legendListeners.size(); i++) {
634
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
635
                }
636
                // getLayers().moveTo(0,0);
637
        }
638

    
639
        /**
640
         * <p>Fires a layer drawing event to all {@link LayerDrawingListener LayerDrawingListener} listeners registered,
641
         *  distinguishing the kind of event.</p>
642
         *
643
         * @param e the event
644
         *
645
         * @see #addLayerDrawingListener(LayerDrawingListener)
646
         * @see #removeLayerDrawListener(LayerDrawingListener)
647
         */
648
        public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
649
                for (int i = 0; i < layerDrawingListeners.size(); i++)
650
                {
651
                        LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
652
                        switch (e.getEventType())
653
                        {
654
                                case LayerDrawEvent.LAYER_BEFORE_DRAW:
655
                                        listener.beforeLayerDraw(e);
656
                                        break;
657
                                case LayerDrawEvent.LAYER_AFTER_DRAW:
658
                                        listener.afterLayerDraw(e);
659
                                        break;
660
                                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
661
                                        listener.beforeGraphicLayerDraw(e);
662
                                        break;
663
                                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
664
                                        listener.afterLayerGraphicDraw(e);
665
                                        break;
666
                        }
667
                }
668
                // getLayers().moveTo(0,0);
669
        }
670

    
671
        /**
672
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
673
         *
674
         * @param e the event with information of the error
675
         *
676
         * @see #addErrorListener(ErrorListener)
677
         * @see #removeErrorListener(LegendListener)
678
         * @see #reportDriverExceptions(String, List)
679
         */
680
        public synchronized void callNewErrorEvent(ErrorEvent e) {
681
                for (int i = 0; i < errorListeners.size(); i++) {
682
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
683
                }
684
                errorListeners.clear();
685
                // getLayers().moveTo(0,0);
686
        }
687

    
688
        /**
689
         * <p>Removes the specified layer listener from this map.</p>
690
         *
691
         * @param listener the listener to remove
692
         *
693
         * @see #addLayerListener(LegendListener)
694
         * @see #callLegendChanged()
695
         */
696
        public void removeLayerListener(LegendListener listener) {
697
                legendListeners.remove(listener);
698
        }
699

    
700
        // SUGERENCIA DE PABLO:
701
        // public void removeLegendListener(LegendListener listener) {
702
        //         legendListeners.remove(listener);
703
        // }
704

    
705
        /**
706
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
707
         *
708
         * @return the hierarchy of nodes of layers stored in this map
709
         */
710
        public FLayers getLayers() {
711
                return layers;
712
        }
713

    
714
        /**
715
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
716
         *
717
         * @param b image with an accessible buffer of image data
718
         */
719
        public void drawLabels(BufferedImage b) {
720
        }
721

    
722
        /**
723
         * @see #redraw()
724
         */
725
        public void invalidate() {
726
                updateDrawVersion();
727
                // Small hack to let the MapControl receive an event and repaint
728
                FLayer layer;
729
                if (layers.getLayersCount() > 0) {
730
                        layer = layers.getLayer(layers.getLayersCount() - 1);
731
                }
732
                else {
733
                        layer = getGraphicsLayer();
734
                }
735
                LayerPositionEvent layerMovedEvent = LayerPositionEvent
736
                                .createLayerMovedEvent(
737
                                                layer, 0, 0);
738
                eventBuffer.layerMoved(layerMovedEvent);
739
        }
740

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

    
772
                Cancellable cancel = new Cancellable() {
773
                        public boolean isCanceled() {
774
                                return false;
775
                        }
776

    
777
                        public void setCanceled(boolean canceled) {
778
                                // No queremos que se pueda cancelar la impresi?n.
779

    
780
                        }
781
                };
782
                this.getMapContextDrawer().print(this.layers, g, cancel, scale,properties);
783
                if (tracLayer != null) {
784
                        tracLayer.draw(null, g, viewPort, cancel, scale);
785
                }
786
        }
787

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

    
799
                return ret;
800
        }
801

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

    
838

    
839
        }
840

    
841
        /**
842
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
843
         *
844
         * @return the new map
845
         */
846
        public MapContext cloneToDraw() {
847
                ViewPort vp;
848
        try {
849
            vp = (ViewPort) getViewPort().clone();
850
            MapContext mapContext=new MapContext(getLayers(),vp);
851
            return mapContext;
852
        } catch (CloneNotSupportedException e) {
853
            throw new RuntimeException(e);
854
        }
855
        }
856

    
857

    
858
        /**
859
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
860
         *
861
         * @param vectorial the layer to add
862
         */
863
        public void addToTrackLayer(FLayer vectorial) {
864
        }
865

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

    
884
                if (viewPort.getImageSize() == null) {
885
                        return -1;
886
                }
887

    
888
                if (viewPort.getAdjustedExtent() == null) {
889
                        return 0;
890
                }
891
                double[] trans2Meter=getDistanceTrans2Meter();
892
                if (proj == null) {
893
                        double w = ((viewPort.getImageSize().width / dpi) * 2.54);
894
                        return (long) (viewPort.getAdjustedExtent().getLength(0) / w * trans2Meter[getViewPort()
895
                                        .getMapUnits()]);
896
                }
897

    
898
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinimum(0)*trans2Meter[getViewPort().getMapUnits()]),
899
                                (viewPort.getAdjustedExtent().getMaximum(0)*trans2Meter[getViewPort().getMapUnits()]),
900
                                viewPort.getImageSize().width, dpi));
901

    
902
        }
903

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

    
934
        /**
935
         * <p>Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
936
         * by default as it is defined in the default Toolkit.</p>
937
         *
938
         * @return double with the screen's dpi
939
         */
940
        public static double getScreenDPI() {
941
                return CompatLocator.getGraphicsUtils().getScreenDPI();
942
        }
943

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

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

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

    
962
        /**
963
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
964
         *      VectorialSubSet)
965
         */
966
        public void select(Visitor visitor) {
967
        }
968

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

    
975
        /**
976
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
977
         */
978
        public void createIndex() {
979
        }
980

    
981
        /**
982
         * @see org.cresques.geo.Projected#getProjection()
983
         *
984
         * @see ViewPort#getProjection()
985
         * @see #setProjection(IProjection)
986
         * @see #reProject(ICoordTrans)
987
         */
988
        public IProjection getProjection() {
989
                return getViewPort().getProjection();
990
        }
991

    
992
        /**
993
         * <p>Sets the new projection.</p>
994
         *
995
         * @param proj the new projection
996
         *
997
         * @see #getProjection()
998
         * @see ViewPort#setProjection(IProjection)
999
         * @see #reProject(ICoordTrans)
1000
         */
1001
        public void setProjection(IProjection proj) {
1002
                if (getViewPort() != null) {
1003
                        getViewPort().setProjection(proj);
1004
                }
1005
        }
1006

    
1007
        /**
1008
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
1009
         */
1010
        public void reProject(ICoordTrans arg0) {
1011
                // TODO implementar reprojecci?n (lo que sea eso)
1012
        }
1013

    
1014

    
1015
        public Envelope getSelectionBounds() throws BaseException {
1016
            
1017
                SelectedEnvelopeVisitor visitor = new SelectedEnvelopeVisitor();
1018
                
1019
                layers.accept(visitor);
1020
                Envelope env_in_data_crs = visitor.getSelectioEnvelope();
1021
                return env_in_data_crs;
1022
        }
1023

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

    
1064
                CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1065

    
1066
                this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1067
        }
1068

    
1069
        /**
1070
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1071
         *
1072
         * @param image image used to accelerate the screen draw
1073
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1074
         * @param cancel shared object that determines if this layer can continue being drawn
1075
         * @param scale value that represents the scale
1076
         * @throws ReadDriverException if fails reading with the driver.
1077
         * @deprecated use {@link #draw(BufferedImage, Graphics2D, Cancellable, double)} instead
1078
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1079
         */
1080
        public void drawGraphics(BufferedImage image, Graphics2D g,
1081
                        Cancellable cancel, double scale) throws ReadException {
1082

    
1083
                // From now on the graphics layer is handled by the MapContextDrawer,
1084
                // so call the draw method instead.
1085
                try {
1086
                        draw(image, g, cancel, scale);
1087
                } catch (MapContextException e) {
1088
                        throw new RuntimeException(e);
1089
                }
1090
        }
1091

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

    
1128
                        public void setCanceled(boolean canceled) {
1129
                                // Do nothing
1130
                        }
1131
                }, scale);
1132
        }
1133

    
1134
        /**
1135
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1136
         *
1137
         * @return the view port
1138
         *
1139
         * @see #setViewPort(ViewPort)
1140
         */
1141
        public ViewPort getViewPort() {
1142
                return viewPort;
1143
        }
1144

    
1145
        /**
1146
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1147
         *  of this map.</p>
1148
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1149
         *  adds the new one.</p>
1150
         *
1151
         * @param viewPort the viewPort
1152
         *
1153
         * @see #getViewPort()
1154
         */
1155
        public void setViewPort(ViewPort viewPort) {
1156
                if (this.viewPort != null) {
1157
                        this.viewPort.removeViewPortListener(eventBuffer);
1158
                }
1159

    
1160
                if (this.mapContextDrawer != null){
1161
                        this.mapContextDrawer.setViewPort(viewPort);
1162
                }
1163

    
1164
                this.viewPort = viewPort;
1165
                if (viewPort != null) {
1166
                        viewPort.addViewPortListener(eventBuffer);
1167
                }
1168
        }
1169

    
1170
        /**
1171
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1172
         *
1173
         * @param extent the extent of the new zoom
1174
         */
1175
        public void zoomToEnvelope(Envelope extent) {
1176
                if (extent!=null) {
1177
                        getViewPort().setEnvelope(extent);
1178
                }
1179
        }
1180

    
1181
        /**
1182
         * <p>Returns the union of all extents of all layers of this map.</p>
1183
         *
1184
         * @return full extent of layers of this map
1185
         * @throws ReadDriverException if the driver fails reading.
1186
         *
1187
         * @see FLayers#getFullEnvelope()
1188
         */
1189
        public Envelope getFullEnvelope() throws ReadException {
1190
                Envelope envelope = layers.getFullEnvelope();
1191
                
1192
                if (tracLayer != null) {
1193
                        Envelope graphicsEnvelope = tracLayer.getFullEnvelope(); 
1194
                        if (envelope == null) {
1195
                                return graphicsEnvelope;
1196
                        }
1197
                        else if (graphicsEnvelope != null) {
1198
                                envelope.add(graphicsEnvelope);
1199
                        }
1200
                }
1201
                
1202
                return envelope;
1203
        }
1204

    
1205

    
1206

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

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

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

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

    
1253
        /**
1254
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1255
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1256
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1257
         *
1258
         * @author Fernando Gonz?lez Cort?s
1259
         */
1260
        public class LayerEventListener implements LayerCollectionListener {
1261
                /*
1262
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1263
                 */
1264
                public void layerAdded(LayerCollectionEvent e) {
1265
                        // Si es la primera capa, fijamos su extent al ViewPort
1266
                        // if (getLayers().getLayersCount() == 1) {
1267
                        if (getViewPort().getEnvelope() == null) {
1268
                                FLayer lyr = e.getAffectedLayer();
1269
                                if (lyr.isAvailable()) {
1270
                                        try {
1271
                                                getViewPort().setEnvelope(lyr.getFullEnvelope());
1272
                                        } catch (ReadException ex) {
1273
                                            logger.error(
1274
                                                MessageFormat.format(
1275
                                                        "Can't set envelope to view port from layer {0}",
1276
                                                        new Object[] {lyr.getName() }
1277
                                                    ),
1278
                                                ex
1279
                                            );
1280
                                        }
1281
                                }
1282
                        }
1283

    
1284
                        // Registramos al FMap como listener del legend de las capas
1285
                        FLayer lyr = e.getAffectedLayer();
1286
                        addSelectionListener(lyr);
1287
                }
1288

    
1289
                /*
1290
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1291
                 */
1292
                public void layerMoved(LayerPositionEvent e) {
1293
                }
1294

    
1295
                /*
1296
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1297
                 */
1298
                public void layerRemoved(LayerCollectionEvent e) {
1299
                        FLayer lyr = e.getAffectedLayer();
1300

    
1301
                        lyr.removeLayerListener(eventBuffer);
1302

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

    
1308
                        if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore()!=null) {
1309
                                ((SingleLayer) lyr).getDataStore().deleteObserver(
1310
                                                MapContext.this);
1311
                        }
1312

    
1313
                }
1314

    
1315
                /*
1316
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1317
                 */
1318
                public void layerAdding(LayerCollectionEvent e)
1319
                                throws CancelationException {
1320
                }
1321

    
1322
                /*
1323
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1324
                 */
1325
                public void layerMoving(LayerPositionEvent e)
1326
                                throws CancelationException {
1327
                }
1328

    
1329
                /*
1330
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1331
                 */
1332
                public void layerRemoving(LayerCollectionEvent e)
1333
                                throws CancelationException {
1334
                }
1335

    
1336

    
1337
                /*
1338
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1339
                 */
1340
                public void visibilityChanged(LayerCollectionEvent e)
1341
                                throws CancelationException {
1342
                }
1343
        }
1344

    
1345
        /**
1346
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1347
         *  collection of layers argument.</p>
1348
         *
1349
         * @param a collection of layers
1350
         */
1351
        public void addAsCollectionListener(FLayers layers2) {
1352
                layers2.addLayerCollectionListener(layerEventListener);
1353
        }
1354

    
1355
        /**
1356
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1357
         *
1358
         * @return the graphic layer of this map
1359
         *
1360
         * @see #setGraphicsLayer(GraphicLayer)
1361
         */
1362
        public GraphicLayer getGraphicsLayer() {
1363
                if (tracLayer == null) {
1364
                        if (getViewPort() != null) {
1365
                                this.tracLayer =
1366
                                                MapContextLocator.getMapContextManager()
1367
                                                                .createGraphicsLayer(
1368
                                                                                getViewPort().getProjection());
1369
                        } else {
1370
                                this.tracLayer =
1371
                                                MapContextLocator.getMapContextManager()
1372
                                                                .createGraphicsLayer(null);
1373
                        }
1374
                }
1375
                return tracLayer;
1376
        }
1377

    
1378
        /**
1379
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1380
         *
1381
         * @param graphicLayer the new graphic layer
1382
         *
1383
         * @see #getGraphicsLayer()
1384
         */
1385
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1386
                tracLayer = graphicLayer;
1387
        }
1388

    
1389
        /**
1390
         * <p>Indicates whether some other object is "equal to" this map.</p>
1391
         * <p>Returns <code>true</code> if success one of this options:
1392
         * <ul>
1393
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1394
         * <li>Both maps have the same layers.
1395
         * <li>Both maps have the same number of layers and with the same name.
1396
         * </ul>
1397
         * </p>
1398
         *
1399
         * @param obj the reference object with which to compare.
1400
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1401
         *
1402
         * @see Object#equals(Object)
1403
         */
1404
        public boolean equals(Object arg0) {
1405
                if (!(arg0 instanceof MapContext)) {
1406
                        return false;
1407
                }
1408
                MapContext map = (MapContext) arg0;
1409
                if (super.equals(arg0)) {
1410
                        return true;
1411
                }
1412
                if (getLayers() == map.getLayers()) {
1413
                        return true;
1414
                }
1415
                boolean isEqual = true;
1416
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1417
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1418

    
1419
                                if (!getLayers().getLayer(i).getName().equals(
1420
                                                map.getLayers().getLayer(i).getName())) {
1421
                                        isEqual = false;
1422
                                }
1423

    
1424
                        }
1425
                } else {
1426
                        isEqual = false;
1427
                }
1428
                return isEqual;
1429
        }
1430

    
1431
        /**
1432
         * <p>Registers the message of an error associated to this map.</p>
1433
         *
1434
         * @param stringProperty the error message
1435
         *
1436
         * @see #getLayersError()
1437
         * @see #clearErrors()
1438
         */
1439
        public void addLayerError(String stringProperty) {
1440
                layersError.add(stringProperty);
1441
        }
1442

    
1443
        /**
1444
         * <p>Gets the list with all error messages registered to this map.</p>
1445
         *
1446
         * @return the list of errors registered to this map
1447
         *
1448
         * @see #addLayerError(String)
1449
         * @see #clearErrors()
1450
         */
1451
        public ArrayList getLayersError() {
1452
                return layersError;
1453
        }
1454

    
1455
        /**
1456
         * <p>Removes all error messages associated to this map.</p>
1457
         *
1458
         * @see #addLayerError(String)
1459
         * @see #getLayersError()
1460
         */
1461
        public void clearErrors() {
1462
                layersError.clear();
1463
        }
1464

    
1465
        /**
1466
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1467
         *
1468
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1469
         * @return the new layer node
1470
         */
1471
        public FLayers getNewGroupLayer(FLayers parent) {
1472
                FLayers group1 = new FLayers();//(this,parent);
1473
                group1.setMapContext(this);
1474
                group1.setParentLayer(parent);
1475
            return group1;
1476
        }
1477

    
1478
        public String getClassName() {
1479
                return null;
1480
        }
1481

    
1482
        public ArrayList getLayersToSnap() {
1483
                return layersToSnap;
1484
        }
1485

    
1486
        public void setLayersToSnap(ArrayList layersToSnap) {
1487
                this.layersToSnap = layersToSnap;
1488

    
1489
        }
1490

    
1491
        public void update(Observable observable, Object notification) {
1492
                // TODO REVISAR ESTO!!!
1493
                String ntype=null;
1494
                if (notification instanceof FeatureStoreNotification) {
1495
                        FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1496
                        ntype =fsNotification.getType();
1497
                        if (
1498
                                        ntype.equals(FeatureStoreNotification.LOAD_FINISHED)||
1499
                                        ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)
1500
                        ) {
1501
                                getLayers().moveTo(0, 0);
1502
                        }
1503
                }
1504
        }
1505

    
1506
        public long getDrawVersion() {
1507
                if (getViewPort().getDrawVersion() > this.viewPortVersion
1508
                                || getLayers().getDrawVersion() > this.layersVersion
1509
                                || getGraphicsLayer().getDrawVersion() > graphicsLayerVersion) {
1510
                        updateDrawVersion();
1511
                }
1512
                return this.drawVersion;
1513
        }
1514

    
1515
        protected void updateDrawVersion(){
1516
                this.layersVersion = getLayers().getDrawVersion();
1517
                this.viewPortVersion = getViewPort().getDrawVersion();
1518
                this.graphicsLayerVersion = getGraphicsLayer().getDrawVersion();
1519
                this.drawVersion++;
1520
        }
1521

    
1522
        public MapContextDrawer getMapContextDrawer() throws ReadException,
1523
            MapContextException {
1524
                if (this.mapContextDrawer == null){
1525
                    if (mapContextDrawerClass == null) {
1526
                this.mapContextDrawer = mapContextManager
1527
                        .createDefaultMapContextDrawerInstance();
1528
            } else {
1529
                this.mapContextDrawer = mapContextManager
1530
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1531
            }
1532
                    this.mapContextDrawer.setMapContext(this);
1533
            this.mapContextDrawer.setViewPort(viewPort);
1534
                }
1535

    
1536
                return this.mapContextDrawer;
1537
        }
1538
        
1539
        public void setMapContextDrawerClass(Class mapContextDrawerClass)
1540
            throws MapContextException {
1541
                mapContextManager.validateMapContextDrawer(mapContextDrawerClass);
1542
                this.mapContextDrawerClass = mapContextDrawerClass;
1543
                if (this.mapContextDrawer != null){
1544
                        this.mapContextDrawer.dispose();
1545
                        this.mapContextDrawer = null;
1546
                }
1547
        }
1548

    
1549
        public void setMapContextDrawer(MapContextDrawer drawer){
1550
                if (this.mapContextDrawer != null){
1551
                        this.mapContextDrawer.dispose();
1552
                        this.mapContextDrawer = null;
1553
                }
1554
                this.mapContextDrawer = drawer;
1555
                if (this.mapContextDrawer != null){
1556
                        this.mapContextDrawer.setMapContext(this);
1557
                        this.mapContextDrawer.setViewPort(viewPort);
1558
                }
1559
        }
1560
        
1561
        public void loadFromState(PersistentState state)
1562
                        throws PersistenceException {
1563
                
1564
                ViewPort vp = (ViewPort) state.get("ViewPort");
1565
                setViewPort(vp);
1566
                
1567
                layers = (FLayers) state.get("layers");
1568
                layers.setName("root layer");
1569
                loadLayers(layers);
1570
                layers.setMapContext(this);
1571
                
1572
                layerEventListener = new LayerEventListener();
1573
                layers.addLayerCollectionListener(layerEventListener);
1574
                
1575
                layers.addLayerCollectionListener(eventBuffer);
1576
                layers.setProjection(vp.getProjection());
1577
                
1578
                //Add the listener for the selection
1579
                addSelectionListener(layers);
1580
        }
1581

    
1582
        private void loadLayers(FLayers lyrs) {
1583
                
1584
                int sz = lyrs.getLayersCount();
1585
                for (int i=0; i<sz; i++) {
1586
                        try {
1587
                                lyrs.getLayer(i).load();
1588
                        } catch (LoadLayerException e) {
1589
                                logger.error("While loading layer: " + lyrs.getLayer(i).getName());
1590
                        }
1591
                }
1592
        }
1593

    
1594
        public void saveToState(PersistentState state) throws PersistenceException {
1595
                state.set("ViewPort", viewPort);
1596
                state.set("layers", layers);
1597
        }
1598

    
1599
        public static void registerPersistent() {
1600
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
1601
                DynStruct definition = manager.addDefinition(
1602
                                MapContext.class,
1603
                                "MapContext",
1604
                                "MapContext Persistence definition",
1605
                                null, 
1606
                                null
1607
                );
1608
                definition.addDynFieldObject("ViewPort")
1609
                        .setClassOfValue(ViewPort.class)
1610
                        .setMandatory(true);
1611
        
1612
                definition.addDynFieldObject("layers")
1613
                        .setClassOfValue(FLayers.class)
1614
                        .setMandatory(true);
1615
        }
1616

    
1617
        protected void doDispose() throws BaseException {
1618
                dispose(layers);
1619
                dispose(tracLayer);
1620
        }
1621
        
1622
    /**
1623
     * <p>Registers an event buffer as a listener for all layers as argument.</p>
1624
     *
1625
     * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1626
     * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1627
     * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1628
     * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1629
     *
1630
     * @param the layer or layers
1631
     */
1632
    private void addSelectionListener(FLayer lyr){
1633
        lyr.addLayerListener(eventBuffer);
1634

    
1635
        if (lyr instanceof Classifiable) {
1636
            Classifiable c = (Classifiable) lyr;
1637
            c.addLegendListener(eventBuffer);
1638
        }
1639

    
1640
        if (lyr instanceof FLayers){
1641
            FLayers lyrs=(FLayers)lyr;
1642
            for(int i=0;i<lyrs.getLayersCount();i++){
1643
                addSelectionListener(lyrs.getLayer(i));
1644
            }
1645
        }
1646
        if (lyr instanceof SingleLayer){
1647
            if (((SingleLayer) lyr).getDataStore() != null) {
1648
                ((SingleLayer) lyr).getDataStore().addObserver(
1649
                        MapContext.this);
1650
            }
1651
        }
1652
    }
1653
}