Statistics
| Revision:

root / branches / v2_0_0_prep / libraries / libFMap_mapcontext / src / org / gvsig / fmap / mapcontext / MapContext.java @ 36467

History | View | Annotate | Download (53 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.text.MessageFormat;
49
import java.util.ArrayList;
50
import java.util.List;
51

    
52
import org.cresques.cts.ICoordTrans;
53
import org.cresques.cts.IProjection;
54
import org.cresques.geo.Projected;
55
import org.slf4j.Logger;
56
import org.slf4j.LoggerFactory;
57

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

    
98

    
99
/**
100
 * <p>The <code>MapContext</code> class represents the model and a part of the control and view around graphical layers
101
 * used by {@link MapControl MapControl}.</p>
102
 *
103
 * <p>An instance of <code>MapContext</code> is made up with:
104
 * <ul>
105
 * <li>a hierarchy of {@link FLayers FLayers} nodes
106
 * <li>a {@link GraphicLayer GraphicLayer} layer
107
 * <li>a {@link ViewPort ViewPort}
108
 * <li>an {@link EventBuffer EventButter}
109
 * <li>some {@link com.iver.cit.gvsig.fmap.layers.LegendListener LegendListener}s
110
 * <li>some {@link LayerDrawingListener LayerDrawingListener}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(MapContext.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 #print(Graphics2D, double, PrintAttributes)
380
         */
381
        private GraphicLayer tracLayer = null;
382
                //MapContextLocator.getMapContextManager().createGraphicsLayer(getProjection());
383

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

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

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

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

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

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

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

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

    
454

    
455

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

    
459
        /**
460
         * <p>Default <i>zoom in</i> factor.</p>
461
         * <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
462
         * area but with the items bigger.</p>
463
         */
464
        public static double ZOOMINFACTOR=2;
465

    
466
        /**
467
         * <p>Default <i>zoom out</i> factor.</p>
468
         * <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
469
         * area but with the items smaller.</p>
470
         */
471
        public static double ZOOMOUTFACTOR=0.5;
472

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

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

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

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

    
498

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

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

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

    
527
        public MapContext() { }
528

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

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

    
546
                setViewPort(vp);
547
        }
548

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
775
                Cancellable cancel = new Cancellable() {
776
                        public boolean isCanceled() {
777
                                return false;
778
                        }
779

    
780
                        public void setCanceled(boolean canceled) {
781
                                // No queremos que se pueda cancelar la impresi?n.
782

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

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

    
802
                return ret;
803
        }
804

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

    
841

    
842
        }
843

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

    
860

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

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

    
887
                if (viewPort.getImageSize() == null) {
888
                        return -1;
889
                }
890

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

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

    
905
        }
906

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

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

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

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

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

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

    
972
        /**
973
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
974
         */
975
        public void selectFromSelection() {
976
        }
977

    
978
        /**
979
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
980
         */
981
        public void createIndex() {
982
        }
983

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

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

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

    
1017
        /**
1018
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#getSelectionBounds()
1019
         *
1020
         * @see SelectedZoomVisitor#getSelectBound()
1021
         */
1022
        public Envelope getSelectionBounds() throws BaseException {
1023
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
1024

    
1025
                layers.accept(visitor);
1026

    
1027
                return visitor.getSelectBound();
1028
        }
1029

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

    
1070
                CompatLocator.getGraphicsUtils().setRenderingHintsForDrawing(g);
1071

    
1072
                this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1073

    
1074
                if (tracLayer != null) {
1075
                        LayerDrawEvent beforeTracLayerEvent =
1076
                                        new LayerDrawEvent(tracLayer, g, viewPort,
1077
                                                        LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1078
                        fireLayerDrawingEvent(beforeTracLayerEvent);
1079
                        tracLayer.draw(image, g, viewPort, cancel, scale);
1080
                        LayerDrawEvent afterTracLayerEvent =
1081
                                        new LayerDrawEvent(tracLayer, g, viewPort,
1082
                                                        LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1083
                        fireLayerDrawingEvent(afterTracLayerEvent);
1084
                }
1085

    
1086
                System.gc();
1087
        }
1088

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

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

    
1146
                        public void setCanceled(boolean canceled) {
1147
                                // Do nothing
1148
                        }
1149
                }, scale);
1150
        }
1151

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

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

    
1178
                if (this.mapContextDrawer != null){
1179
                        this.mapContextDrawer.setViewPort(viewPort);
1180
                }
1181

    
1182
                this.viewPort = viewPort;
1183
                if (viewPort != null) {
1184
                        viewPort.addViewPortListener(eventBuffer);
1185
                }
1186
        }
1187

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

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

    
1211

    
1212

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

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

    
1241
        /**
1242
         * @see EventBuffer#beginAtomicEvent()
1243
         *
1244
         * @see #endAtomicEvent()
1245
         */
1246
        public void beginAtomicEvent() {
1247
                eventBuffer.beginAtomicEvent();
1248
        }
1249

    
1250
        /**
1251
         * @see EventBuffer#endAtomicEvent()
1252
         *
1253
         * @see #beginAtomicEvent()
1254
         */
1255
        public void endAtomicEvent() {
1256
                eventBuffer.endAtomicEvent();
1257
        }
1258

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

    
1290
                        // Registramos al FMap como listener del legend de las capas
1291
                        FLayer lyr = e.getAffectedLayer();
1292
                        addSelectionListener(lyr);
1293
                }
1294

    
1295
                /*
1296
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1297
                 */
1298
                public void layerMoved(LayerPositionEvent e) {
1299
                }
1300

    
1301
                /*
1302
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1303
                 */
1304
                public void layerRemoved(LayerCollectionEvent e) {
1305
                        FLayer lyr = e.getAffectedLayer();
1306

    
1307
                        lyr.removeLayerListener(eventBuffer);
1308

    
1309
                        if (lyr instanceof Classifiable) {
1310
                                Classifiable c = (Classifiable) lyr;
1311
                                c.removeLegendListener(eventBuffer);
1312
                        }
1313

    
1314
                        if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore()!=null) {
1315
                                ((SingleLayer) lyr).getDataStore().deleteObserver(
1316
                                                MapContext.this);
1317
                        }
1318

    
1319
                }
1320

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

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

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

    
1342

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

    
1351
        /**
1352
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1353
         *  collection of layers argument.</p>
1354
         *
1355
         * @param a collection of layers
1356
         */
1357
        public void addAsCollectionListener(FLayers layers2) {
1358
                layers2.addLayerCollectionListener(layerEventListener);
1359
        }
1360

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

    
1384
        /**
1385
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1386
         *
1387
         * @param graphicLayer the new graphic layer
1388
         *
1389
         * @see #getGraphicsLayer()
1390
         */
1391
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1392
                tracLayer = graphicLayer;
1393
        }
1394

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

    
1425
                                if (!getLayers().getLayer(i).getName().equals(
1426
                                                map.getLayers().getLayer(i).getName())) {
1427
                                        isEqual = false;
1428
                                }
1429

    
1430
                        }
1431
                } else {
1432
                        isEqual = false;
1433
                }
1434
                return isEqual;
1435
        }
1436

    
1437
        /**
1438
         * <p>Registers the message of an error associated to this map.</p>
1439
         *
1440
         * @param stringProperty the error message
1441
         *
1442
         * @see #getLayersError()
1443
         * @see #clearErrors()
1444
         */
1445
        public void addLayerError(String stringProperty) {
1446
                layersError.add(stringProperty);
1447
        }
1448

    
1449
        /**
1450
         * <p>Gets the list with all error messages registered to this map.</p>
1451
         *
1452
         * @return the list of errors registered to this map
1453
         *
1454
         * @see #addLayerError(String)
1455
         * @see #clearErrors()
1456
         */
1457
        public ArrayList getLayersError() {
1458
                return layersError;
1459
        }
1460

    
1461
        /**
1462
         * <p>Removes all error messages associated to this map.</p>
1463
         *
1464
         * @see #addLayerError(String)
1465
         * @see #getLayersError()
1466
         */
1467
        public void clearErrors() {
1468
                layersError.clear();
1469
        }
1470

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

    
1484
        public String getClassName() {
1485
                return null;
1486
        }
1487

    
1488
        public ArrayList getLayersToSnap() {
1489
                return layersToSnap;
1490
        }
1491

    
1492
        public void setLayersToSnap(ArrayList layersToSnap) {
1493
                this.layersToSnap = layersToSnap;
1494

    
1495
        }
1496

    
1497
        public void update(Observable observable, Object notification) {
1498
                // TODO REVISAR ESTO!!!
1499
                String ntype=null;
1500
                if (notification instanceof FeatureStoreNotification) {
1501
                        FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1502
                        ntype =fsNotification.getType();
1503
                        if (
1504
                                        ntype.equals(FeatureStoreNotification.LOAD_FINISHED)||
1505
                                        ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)
1506
                        ) {
1507
                                getLayers().moveTo(0, 0);
1508
                        }
1509
                }
1510
        }
1511

    
1512
        public long getDrawVersion() {
1513
                return this.drawVersion;
1514
        }
1515

    
1516
        protected void updateDrawVersion(){
1517
                this.drawVersion++;
1518
        }
1519

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

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

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

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

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

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

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

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

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