Statistics
| Revision:

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

History | View | Annotate | Download (54.3 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.*;
44
import java.awt.geom.Rectangle2D;
45
import java.awt.image.BufferedImage;
46
import java.util.ArrayList;
47
import java.util.List;
48
import java.util.prefs.Preferences;
49

    
50
import org.cresques.cts.ICoordTrans;
51
import org.cresques.cts.IProjection;
52
import org.cresques.geo.Projected;
53
import org.gvsig.compat.print.PrintAttributes;
54
import org.gvsig.fmap.dal.exception.ReadException;
55
import org.gvsig.fmap.dal.feature.FeatureStoreNotification;
56
import org.gvsig.fmap.geom.GeometryLocator;
57
import org.gvsig.fmap.geom.GeometryManager;
58
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
59
import org.gvsig.fmap.geom.exception.CreateEnvelopeException;
60
import org.gvsig.fmap.geom.primitive.Envelope;
61
import org.gvsig.fmap.mapcontext.events.ErrorEvent;
62
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
63
import org.gvsig.fmap.mapcontext.events.listeners.ErrorListener;
64
import org.gvsig.fmap.mapcontext.events.listeners.EventBuffer;
65
import org.gvsig.fmap.mapcontext.impl.InvalidMapContextDrawerClassException;
66
import org.gvsig.fmap.mapcontext.layers.*;
67
import org.gvsig.fmap.mapcontext.layers.operations.Classifiable;
68
import org.gvsig.fmap.mapcontext.layers.operations.SingleLayer;
69
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
70
import org.gvsig.fmap.mapcontext.rendering.strategies.SelectedZoomVisitor;
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.task.Cancellable;
75
import org.gvsig.tools.visitor.Visitor;
76
import org.slf4j.Logger;
77
import org.slf4j.LoggerFactory;
78

    
79
import com.iver.utiles.IPersistence;
80
import com.iver.utiles.XMLEntity;
81
import com.iver.utiles.XMLException;
82

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

    
141

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

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

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

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

    
171

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

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

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

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

    
309
        /* Do not alter the order and the values of this array, if you need append values.*/
310
        /**
311
         * <p>Gets the name of all distance measurements supported by <code>MapContext</code>.</p>
312
         */
313
//        public static final String[] NAMES= {
314
//                Messages.getString("Kilometros"),
315
//                Messages.getString("Metros"),
316
//                Messages.getString("Centimetros"),
317
//                Messages.getString("Milimetros"),
318
//                Messages.getString("Millas"),
319
//                Messages.getString("Yardas"),
320
//                Messages.getString("Pies"),
321
//                Messages.getString("Pulgadas"),
322
//                Messages.getString("Grados"),
323
//        };
324

    
325
        public static final int EQUALS = 0;
326

    
327
        public static final int DISJOINT = 1;
328

    
329
        public static final int INTERSECTS = 2;
330

    
331
        public static final int TOUCHES = 3;
332

    
333
        public static final int CROSSES = 4;
334

    
335
        public static final int WITHIN = 5;
336

    
337
        public static final int CONTAINS = 6;
338

    
339
        public static final int OVERLAPS = 7;
340

    
341
        /**
342
         * A hierarchy of {@link FLayers FLayers} nodes.
343
         *
344
         * @see #getLayers()
345
         * @see #print(Graphics2D, double, PrintAttributes)
346
         */
347
        protected FLayers layers;
348

    
349
        /**
350
         * A layer with graphical items: geometries and symbols.
351
         *
352
         * @see #getGraphicsLayer()
353
         * @see #setGraphicsLayer(GraphicLayer)
354
         * @see #drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
355
         * @see #print(Graphics2D, double, PrintAttributes)
356
         */
357
        private GraphicLayer tracLayer = new GraphicLayer();
358

    
359
        /**
360
         * Information for draw layers in a view.
361
         *
362
         * @see #getViewPort()
363
         * @see #setViewPort(ViewPort)
364
         */
365
        private ViewPort viewPort;
366

    
367
        // private ArrayList invalidationListeners = new ArrayList();
368

    
369
        /**
370
         * Array list with all {@link LegendListener LegendListener} registered to this map.
371
         *
372
         * @see #addLayerListener(LegendListener)
373
         * @see #removeLayerListener(LegendListener)
374
         * @see #callLegendChanged()
375
         */
376
        private ArrayList legendListeners = new ArrayList();
377

    
378
        /**
379
         * Array list with all {@link LayerDrawingListener LayerDrawingListener} registered to this map.
380
         *
381
         * @see #addLayerDrawingListener(LayerDrawingListener)
382
         * @see #removeLayerDrawListener(LayerDrawingListener)
383
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
384
         */
385
        private ArrayList layerDrawingListeners = new ArrayList();
386

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

    
406
        /**
407
         * Event listener for the collection of layers of this map.
408
         */
409
        private LayerEventListener layerEventListener = null;
410

    
411
        /**
412
         * List with information of all errors produced on all layers.
413
         *
414
         * @see #addLayerError(String)
415
         * @see #getLayersError()
416
         * @see #clearErrors()
417
         */
418
        private ArrayList layersError = new ArrayList();
419

    
420
        /**
421
         * Array list with all {@link ErrorListener ErrorListener} registered to this map.
422
         *
423
         * @see #addErrorListener(ErrorListener)
424
         * @see #removeErrorListener(LegendListener)
425
         * @see #callNewErrorEvent(ErrorEvent)
426
         * @see #reportDriverExceptions(String, List)
427
         */
428
        private ArrayList errorListeners = new ArrayList();
429

    
430

    
431

    
432
        // public static ResourceBundle myResourceBundle =
433
        // ResourceBundle.getBundle("FMap");
434

    
435
        /**
436
         * <p>Default <i>zoom in</i> factor.</p>
437
         * <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
438
         * area but with the items bigger.</p>
439
         */
440
        public static double ZOOMINFACTOR=2;
441

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

    
449
        /**
450
         *          * Draw version of the context. It's used for know when de componend has
451
         * changed any visualization property
452
         *
453
         *  @see getDrawVersion
454
         *  @see updateDrawVersion
455
         */
456
        private long drawVersion= 0L;
457

    
458
        /**
459
         * Object to Manage Draw of MapContext
460
         */
461
        private MapContextDrawer mapContextDrawer= null;
462

    
463
        /**
464
         * Object to Manage Draw of MapContext
465
         */
466
        private Class mapContextDrawerClass = null;
467

    
468
        /**
469
         * <p>Color used to represent the selections.</p>
470
         */
471
        private static Color selectionColor = Color.YELLOW;
472
//        private Observable DefaultObservable defaultObservable=new DefaultObservable();
473

    
474
        private ArrayList snappers = new ArrayList();
475
        private ArrayList layersToSnap = new ArrayList();
476

    
477

    
478
        /**
479
         * <p>Gets the color used to represent the selections.</p>
480
         *
481
         * @return color used to represent the selections
482
         */
483
        public static Color getSelectionColor() {
484
                return selectionColor;
485
        }
486

    
487
        /**
488
         * <p>Sets the color used to represent the selections.</p>
489
         *
490
         * @param selectionColor color used to represent the selections
491
         */
492
        public static void setSelectionColor(Color selectionColor) {
493
                MapContext.selectionColor = selectionColor;
494
        }
495

    
496
        /**
497
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
498
         *  without layers.</p>
499
         *
500
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
501
         */
502
        public MapContext(ViewPort vp) {
503
                this.layers = new FLayers();//(this,null);
504
                this.layers.setMapContext(this);
505

    
506
                layerEventListener = new LayerEventListener();
507
                layers.addLayerCollectionListener(layerEventListener);
508
                layers.addLayerCollectionListener(eventBuffer);
509

    
510
                setViewPort(vp);
511

    
512
        }
513

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

    
523
                layerEventListener = new LayerEventListener();
524
                layers.addLayerCollectionListener(layerEventListener);
525
                layers.addLayerCollectionListener(eventBuffer);
526

    
527
                setViewPort(vp);
528
        }
529

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

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

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

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

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

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

    
617
        // SUGERENCIA DE PABLO:
618
        //public void removeErrorListener(ErrorListener listener) {
619
        //        errorListeners.remove(listener);
620
        //}
621

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

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

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

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

    
697
        // SUGERENCIA DE PABLO:
698
        // public void removeLegendListener(LegendListener listener) {
699
        //         legendListeners.remove(listener);
700
        // }
701

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

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

    
719
        /**
720
         * @see #redraw()
721
         */
722
        public void invalidate() {
723
                if (getLayers().getLayersCount() > 0) {
724
                        getLayers().moveTo(0, 0);
725
                }
726
        }
727

    
728
    /**
729
     * <p>
730
     * Prints the layers of this map using the {@link Graphics2D Graphics2D}
731
     * argument, that usually is the {@link Graphics Graphics} of the printer.
732
     * </p>
733
     * 
734
     * @param g
735
     *            for rendering 2-dimensional shapes, text and images on the
736
     *            Java(tm) platform
737
     * @param scale
738
     *            the scale of the view. Must be between
739
     *            {@linkplain FLayer#getMinScale()} and
740
     *            {@linkplain FLayer#getMaxScale()}.
741
     * @param properties
742
     *            a set with the settings to be applied to a whole print job and
743
     *            to all the documents in the print job
744
     * @throws MapContextException
745
     *             if there is an error getting the instance of MapContextDrawer
746
     * 
747
     * @throws ReadDriverException
748
     *             if fails reading with driver.
749
     * 
750
     * @see FLayers#print(Graphics2D, ViewPort, Cancellable, double,
751
     *      PrintAttributes)
752
     * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable,
753
     *      double)
754
     */
755
        public void print(Graphics2D g, double scale,
756
                        PrintAttributes properties) throws ReadException,
757
            MapContextException {
758
                RenderingHints renderHints = new RenderingHints(
759
                                RenderingHints.KEY_ANTIALIASING,
760
                                RenderingHints.VALUE_ANTIALIAS_ON);
761
                renderHints.put(RenderingHints.KEY_RENDERING,
762
                                RenderingHints.VALUE_RENDER_QUALITY);
763
                g.setRenderingHints(renderHints);
764

    
765
                Cancellable cancel = new Cancellable() {
766
                        public boolean isCanceled() {
767
                                return false;
768
                        }
769

    
770
                        public void setCanceled(boolean canceled) {
771
                                // No queremos que se pueda cancelar la impresi?n.
772

    
773
                        }
774
                };
775
                this.getMapContextDrawer().print(this.layers, g, cancel, scale,properties);
776
                tracLayer.draw(null, g, viewPort, cancel, scale);
777
        }
778

    
779
        /**
780
         * <p>Returns a new <code>MapContext</code> instance with the information of the <code>vp</code> argument, and the layers of this map.</p>
781
         *
782
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
783
         *
784
         * @return a new <code>MapContext</code> instance projected by <code>vp</code>
785
         */
786
        public MapContext createNewFMap(ViewPort vp) {
787
                MapContext ret = new MapContext(vp);
788
                ret.layers = this.layers;
789

    
790
                return ret;
791
        }
792

    
793
        /**
794
         * <p>Creates a new independent <code>MapContext</code> instance, that has a clone of the layers and the view port of this one.</p>
795
         * <p>The new map will have the same data source drivers to avoid waste memory, and work faster.</p>
796
         *
797
         * @return the new <code>MapContext</code> instance
798
         *
799
         * @throws XMLException if fails cloning the view port or a layer
800
         *
801
         * @see FLayer#cloneLayer()
802
         * @see ViewPort#cloneViewPort()
803
         */
804
        public MapContext cloneFMap() {
805
                ViewPort vp = getViewPort().cloneViewPort();
806
                FLayers antLayers = getLayers();
807
                MapContext ret = new MapContext(vp);
808
                FLayers aux = new FLayers();//(ret,null);
809
                aux.setMapContext(ret);
810
                for (int i=0; i < antLayers.getLayersCount(); i++)
811
                {
812
                        FLayer lyr = antLayers.getLayer(i);
813
                        try {
814
                                aux.addLayer(lyr.cloneLayer());
815
                        } catch (Exception e) {
816

    
817
                                // TODO Auto-generated catch block
818
                                e.printStackTrace();
819
                                // FIXME
820
                                throw new RuntimeException(e);
821
                        }
822
                }
823
                ret.layers = aux;
824
                return ret;
825

    
826
//                return createFromXML(getXMLEntity());
827

    
828
        }
829

    
830
        /**
831
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
832
         *
833
         * @return the new map
834
         */
835
        public MapContext cloneToDraw() {
836
                ViewPort vp = getViewPort().cloneViewPort();
837
                MapContext mapContext=new MapContext(getLayers(),vp);
838
                return mapContext;
839
        }
840

    
841
        /**
842
         * A?ade la capa que se pasa como par?metro al nodo que se pasa como
843
         * parametro y lanza ProjectionMismatchException si no est?n todas las capas
844
         * de este FMap en la misma proyecci?n. Lanza un ChildNotAllowedException si
845
         * la capa no es un FLayers y no permite hijos
846
         *
847
         * @param vectorial
848
         *            DOCUMENT ME!
849
         */
850

    
851
        /*
852
         * public void addLayer(LayerPath parent, FLayer layer) throws
853
         * ProjectionMismatchException, ChildrenNotAllowedException {
854
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
855
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
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().getWidth() / 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()]), viewPort.getImageSize()
900
                                                .getWidth(), 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
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
942
                Toolkit kit = Toolkit.getDefaultToolkit();
943
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
944
                return dpi;
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.getExtent() == null) {
1067
                        // System.err.println("viewPort.getExtent() = null");
1068
                        return;
1069
                }
1070
                System.out.println("Viewport despues: " + viewPort.toString());
1071
                /*
1072
                 * if ((viewPort.getImageWidth() <=0) || (viewPort.getImageHeight() <=
1073
                 * 0)) { return; }
1074
                 */
1075

    
1076
//                prepareDrawing(image, g, scale);
1077

    
1078
                // M?s c?lidad al texto
1079
                RenderingHints renderHints = new RenderingHints(
1080
                                RenderingHints.KEY_ANTIALIASING,
1081
                                RenderingHints.VALUE_ANTIALIAS_ON);
1082
                renderHints.put(RenderingHints.KEY_RENDERING,
1083
                                RenderingHints.VALUE_RENDER_QUALITY);
1084
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
1085
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1086
                g.setRenderingHints(renderHints);
1087

    
1088
                long t1 = System.currentTimeMillis();
1089
//                layers.draw(image, g, viewPort, cancel, scale);
1090

    
1091
                this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1092

    
1093
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
1094
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1095
                fireLayerDrawingEvent(beforeTracLayerEvent);
1096
                tracLayer.draw(image, g, viewPort, cancel, scale);
1097
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
1098
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1099
                fireLayerDrawingEvent(afterTracLayerEvent);
1100

    
1101
                //layers.setDirty(false);
1102
                long t2 = System.currentTimeMillis();
1103
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
1104
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
1105
                /*
1106
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
1107
                 * GeneralPath(viewPort.getExtent());
1108
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
1109
                 */
1110
                System.gc();
1111
        }
1112

    
1113
        /**
1114
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1115
         *
1116
         * @param image image used to accelerate the screen draw
1117
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1118
         * @param cancel shared object that determines if this layer can continue being drawn
1119
         * @param scale value that represents the scale
1120
         * @throws ReadDriverException if fails reading with the driver.
1121
         *
1122
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1123
         */
1124
        public void drawGraphics(BufferedImage image, Graphics2D g,
1125
                        Cancellable cancel, double scale) throws ReadException {
1126
                if (viewPort == null) {
1127
                        return;
1128
                }
1129
                tracLayer.draw(image, g, viewPort, cancel, scale);
1130
        }
1131

    
1132
    /**
1133
     * <p>
1134
     * Like
1135
     * {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}
1136
     * , but creating the task as cancellable.
1137
     * </p>
1138
     * 
1139
     * @param image
1140
     *            buffer used sometimes instead <code>g</code> to accelerate the
1141
     *            draw. For example, if two points are as closed that can't be
1142
     *            distinguished, draws only one.
1143
     * @param g
1144
     *            for rendering 2-dimensional shapes, text and images on the
1145
     *            Java(tm) platform
1146
     * @param scale
1147
     *            the scale of the view. Must be between
1148
     *            {@linkplain FLayer#getMinScale()} and
1149
     *            {@linkplain FLayer#getMaxScale()}.
1150
     * @throws MapContextException
1151
     *             if there is an error getting the instance of MapContextDrawer
1152
     * 
1153
     * @throws ReadDriverException
1154
     *             if the driver fails reading.
1155
     * 
1156
     * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1157
     */
1158
        public void draw(BufferedImage image, Graphics2D g, double scale)
1159
                        throws ReadException, MapContextException {
1160
                //layers.setDirty(true);
1161
                draw(image, g, new Cancellable() {
1162
                        /**
1163
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1164
                         */
1165
                        public boolean isCanceled() {
1166
                                return false;
1167
                        }
1168

    
1169
                        public void setCanceled(boolean canceled) {
1170
                                // TODO Auto-generated method stub
1171

    
1172
                        }
1173
                }, scale);
1174
        }
1175

    
1176
        /**
1177
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1178
         *
1179
         * @return the view port
1180
         *
1181
         * @see #setViewPort(ViewPort)
1182
         */
1183
        public ViewPort getViewPort() {
1184
                return viewPort;
1185
        }
1186

    
1187
        /**
1188
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1189
         *  of this map.</p>
1190
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1191
         *  adds the new one.</p>
1192
         *
1193
         * @param viewPort the viewPort
1194
         *
1195
         * @see #getViewPort()
1196
         */
1197
        public void setViewPort(ViewPort viewPort) {
1198
                if (this.viewPort != null) {
1199
                        this.viewPort.removeViewPortListener(eventBuffer);
1200
                }
1201

    
1202
                if (this.mapContextDrawer != null){
1203
                        this.mapContextDrawer.setViewPort(viewPort);
1204
                }
1205

    
1206
                this.viewPort = viewPort;
1207
                if (viewPort != null) {
1208
                        viewPort.addViewPortListener(eventBuffer);
1209
                }
1210
        }
1211

    
1212
        /**
1213
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1214
         *
1215
         * @param extent the extent of the new zoom
1216
         */
1217
        public void zoomToEnvelope(Envelope extent) {
1218
                if (extent!=null) {
1219
                        getViewPort().setEnvelope(extent);
1220
                }
1221
        }
1222

    
1223
        /**
1224
         * <p>Returns the union of all extents of all layers of this map.</p>
1225
         *
1226
         * @return full extent of layers of this map
1227
         * @throws ReadDriverException if the driver fails reading.
1228
         *
1229
         * @see FLayers#getFullEnvelope()
1230
         */
1231
        public Envelope getFullEnvelope() throws ReadException {
1232
                return layers.getFullEnvelope();
1233
        }
1234

    
1235
        /**
1236
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1237
         * <ul>
1238
         * <li>XML entity of the internal {@link ViewPort ViewPort}.
1239
         * <li>XML entity of the internal {@link FLayers FLayers}.
1240
         * </ul>
1241
         *
1242
         * @return XMLEntity the XML entity
1243
         * @throws XMLException
1244
         * @throws XMLException if there is any error creating the XML from the map.
1245
         *
1246
         * @see #createFromXML(XMLEntity)
1247
         * @see ViewPort#getXMLEntity()
1248
         * @see FLayers#getXMLEntity()
1249
         */
1250
        public XMLEntity getXMLEntity() throws XMLException {
1251
                XMLEntity xml = new XMLEntity();
1252
                xml.putProperty("className", this.getClass().getName());
1253
                xml.addChild(viewPort.getXMLEntity());
1254
                xml.addChild(layers.getXMLEntity());
1255

    
1256
                return xml;
1257
        }
1258

    
1259
        /**
1260
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1261
         *  with the data of the {@link ViewPort ViewPort} and
1262
         *  {@link FLayers FLayers}.</p>
1263
         *
1264
         * @param xml an XML entity
1265
         *
1266
         * @return the new <code>MapContext</code> instance
1267
         *
1268
         * @throws XMLException if there is any error creating the map from the XML.
1269
         *
1270
         * @see #getXMLEntity()
1271
         * @see ViewPort#createFromXML(XMLEntity)
1272
         * @see FLayers#setXMLEntity(XMLEntity)
1273
         */
1274
        public static MapContext createFromXML(XMLEntity xml) throws XMLException {
1275
                ViewPort vp = ViewPort.createFromXML(xml.getChild(0));
1276
                MapContext fmap = new MapContext(vp);
1277
                fmap.layers.setXMLEntity(xml.getChild(1));
1278
                fmap.layers.setName("root layer");
1279
                return fmap;
1280
        }
1281

    
1282
        /**
1283
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1284
         *
1285
         * @param listener the new listener
1286
         *
1287
         * @return <code>true</code> if has added the listener successfully
1288
         *
1289
         * @see #removeAtomicEventListener(AtomicEventListener)
1290
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1291
         */
1292
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1293
                return eventBuffer.addAtomicEventListener(listener);
1294
        }
1295

    
1296
        /**
1297
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1298
         *
1299
         * @param listener the listener to remove
1300
         *
1301
     * @return <tt>true</tt> if the list contained the specified element
1302
         *
1303
         * @see #addAtomicEventListener(AtomicEventListener)
1304
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1305
         */
1306
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1307
                return eventBuffer.removeAtomicEventListener(listener);
1308
        }
1309

    
1310
        /**
1311
         * @see EventBuffer#beginAtomicEvent()
1312
         *
1313
         * @see #endAtomicEvent()
1314
         */
1315
        public void beginAtomicEvent() {
1316
                eventBuffer.beginAtomicEvent();
1317
        }
1318

    
1319
        /**
1320
         * @see EventBuffer#endAtomicEvent()
1321
         *
1322
         * @see #beginAtomicEvent()
1323
         */
1324
        public void endAtomicEvent() {
1325
                eventBuffer.endAtomicEvent();
1326
        }
1327

    
1328
        /**
1329
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1330
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1331
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1332
         *
1333
         * @author Fernando Gonz?lez Cort?s
1334
         */
1335
        public class LayerEventListener implements LayerCollectionListener {
1336
                /*
1337
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1338
                 */
1339
                public void layerAdded(LayerCollectionEvent e) {
1340
                        // Si es la primera capa, fijamos su extent al ViewPort
1341
                        // if (getLayers().getLayersCount() == 1) {
1342
                        if (getViewPort().getExtent() == null) {
1343
                                FLayer lyr = e.getAffectedLayer();
1344
                                if (lyr.isAvailable()) {
1345
                                        try {
1346
                                                getViewPort().setEnvelope(lyr.getFullEnvelope());
1347
                                        } catch (ReadException e1) {
1348
                                                e1.printStackTrace();
1349
                                        }
1350
                                }
1351
                        }
1352

    
1353
                        // Registramos al FMap como listener del legend de las capas
1354
                        FLayer lyr = e.getAffectedLayer();
1355
                        selectionListener(lyr);
1356
                        if (lyr instanceof SingleLayer){
1357
                                if (((SingleLayer) lyr).getDataStore() != null) {
1358
                                        ((SingleLayer) lyr).getDataStore().addObserver(
1359
                                                        MapContext.this);
1360
                                }
1361
                        }
1362
                }
1363

    
1364
                /**
1365
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1366
                 *
1367
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1368
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1369
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1370
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1371
                 *
1372
                 * @param the layer or layers
1373
                 */
1374
                private void selectionListener(FLayer lyr){
1375
                        lyr.addLayerListener(eventBuffer);
1376

    
1377
                        if (lyr instanceof Classifiable) {
1378
                                Classifiable c = (Classifiable) lyr;
1379
                                c.addLegendListener(eventBuffer);
1380
                        }
1381

    
1382
//                        if (lyr instanceof AlphanumericData) {
1383
//                                Selectable s=null;
1384
//                                try {
1385
//                                        s = ((AlphanumericData) lyr).getRecordset();
1386
//                                        if (s!=null) {
1387
//                                                s.addSelectionListener(eventBuffer);
1388
//                                        }
1389
//                                } catch (ReadException e1) {
1390
//                                        e1.printStackTrace();
1391
//                                }
1392
//
1393
//                        }
1394
                        if (lyr instanceof FLayers){
1395
                                FLayers lyrs=(FLayers)lyr;
1396
                                for(int i=0;i<lyrs.getLayersCount();i++){
1397
                                        selectionListener(lyrs.getLayer(i));
1398
                                }
1399
                        }
1400

    
1401
                }
1402
                /*
1403
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1404
                 */
1405
                public void layerMoved(LayerPositionEvent e) {
1406
                }
1407

    
1408
                /*
1409
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1410
                 */
1411
                public void layerRemoved(LayerCollectionEvent e) {
1412
                        FLayer lyr = e.getAffectedLayer();
1413

    
1414
                        lyr.removeLayerListener(eventBuffer);
1415

    
1416
                        if (lyr instanceof Classifiable) {
1417
                                Classifiable c = (Classifiable) lyr;
1418
                                c.removeLegendListener(eventBuffer);
1419
                        }
1420

    
1421
                        if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore()!=null) {
1422
                                ((SingleLayer) lyr).getDataStore().deleteObserver(
1423
                                                MapContext.this);
1424
                        }
1425

    
1426
//                        if (lyr instanceof FLyrVect ) {
1427
//                                Selectable s = (Selectable) lyr;
1428
//                                s.addSelectionListener(eventBuffer);
1429
//                        }
1430
                }
1431

    
1432
                /*
1433
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1434
                 */
1435
                public void layerAdding(LayerCollectionEvent e)
1436
                                throws CancelationException {
1437
                }
1438

    
1439
                /*
1440
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1441
                 */
1442
                public void layerMoving(LayerPositionEvent e)
1443
                                throws CancelationException {
1444
                }
1445

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

    
1453

    
1454
                /*
1455
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1456
                 */
1457
                public void visibilityChanged(LayerCollectionEvent e)
1458
                                throws CancelationException {
1459
                }
1460
        }
1461

    
1462
        /**
1463
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1464
         *  collection of layers argument.</p>
1465
         *
1466
         * @param a collection of layers
1467
         */
1468
        public void addAsCollectionListener(FLayers layers2) {
1469
                layers2.addLayerCollectionListener(layerEventListener);
1470
        }
1471

    
1472
        /**
1473
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1474
         *
1475
         * @return the graphic layer of this map
1476
         *
1477
         * @see #setGraphicsLayer(GraphicLayer)
1478
         */
1479
        public GraphicLayer getGraphicsLayer() {
1480
                return tracLayer;
1481
        }
1482

    
1483
        /**
1484
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1485
         *
1486
         * @param graphicLayer the new graphic layer
1487
         *
1488
         * @see #getGraphicsLayer()
1489
         */
1490
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1491
                tracLayer = graphicLayer;
1492
        }
1493

    
1494
        /**
1495
         * <p>Indicates whether some other object is "equal to" this map.</p>
1496
         * <p>Returns <code>true</code> if success one of this options:
1497
         * <ul>
1498
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1499
         * <li>Both maps have the same layers.
1500
         * <li>Both maps have the same number of layers and with the same name.
1501
         * </ul>
1502
         * </p>
1503
         *
1504
         * @param obj the reference object with which to compare.
1505
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1506
         *
1507
         * @see Object#equals(Object)
1508
         */
1509
        public boolean equals(Object arg0) {
1510
                if (!(arg0 instanceof MapContext)) {
1511
                        return false;
1512
                }
1513
                MapContext map = (MapContext) arg0;
1514
                if (super.equals(arg0)) {
1515
                        return true;
1516
                }
1517
                if (getLayers() == map.getLayers()) {
1518
                        return true;
1519
                }
1520
                boolean isEqual = true;
1521
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1522
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1523

    
1524
                                if (!getLayers().getLayer(i).getName().equals(
1525
                                                map.getLayers().getLayer(i).getName())) {
1526
                                        isEqual = false;
1527
                                }
1528

    
1529
                        }
1530
                } else {
1531
                        isEqual = false;
1532
                }
1533
                return isEqual;
1534
        }
1535

    
1536
        /**
1537
         * <p>Registers the message of an error associated to this map.</p>
1538
         *
1539
         * @param stringProperty the error message
1540
         *
1541
         * @see #getLayersError()
1542
         * @see #clearErrors()
1543
         */
1544
        public void addLayerError(String stringProperty) {
1545
                layersError.add(stringProperty);
1546
        }
1547

    
1548
        /**
1549
         * <p>Gets the list with all error messages registered to this map.</p>
1550
         *
1551
         * @return the list of errors registered to this map
1552
         *
1553
         * @see #addLayerError(String)
1554
         * @see #clearErrors()
1555
         */
1556
        public ArrayList getLayersError() {
1557
                return layersError;
1558
        }
1559

    
1560
        /**
1561
         * <p>Removes all error messages associated to this map.</p>
1562
         *
1563
         * @see #addLayerError(String)
1564
         * @see #getLayersError()
1565
         */
1566
        public void clearErrors() {
1567
                layersError.clear();
1568
        }
1569

    
1570
        /**
1571
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1572
         *
1573
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1574
         * @return the new layer node
1575
         */
1576
        public FLayers getNewGroupLayer(FLayers parent) {
1577
                FLayers group1 = new FLayers();//(this,parent);
1578
                group1.setMapContext(this);
1579
                group1.setParentLayer(parent);
1580
            return group1;
1581
        }
1582

    
1583
        public String getClassName() {
1584
                return null;
1585
        }
1586

    
1587
        public void setXMLEntity(XMLEntity arg0) {
1588
                // TODO Auto-generated method stub
1589

    
1590
        }
1591

    
1592
        public ArrayList getSnappers() {
1593
                return snappers;
1594
        }
1595
        public void setSnappers(ArrayList snappers){
1596
                this.snappers=snappers;
1597
        }
1598

    
1599

    
1600
        public ArrayList getLayersToSnap() {
1601
                return layersToSnap;
1602
        }
1603

    
1604
        public void setLayersToSnap(ArrayList layersToSnap) {
1605
                this.layersToSnap = layersToSnap;
1606

    
1607
        }
1608

    
1609
        public void update(Observable observable, Object notification) {
1610
                // TODO REVISAR ESTO!!!
1611
                String ntype=null;
1612
                if (notification instanceof FeatureStoreNotification) {
1613
                        FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1614
                        ntype =fsNotification.getType();
1615
                        if (
1616
                                        ntype.equals(FeatureStoreNotification.LOAD_FINISHED)||
1617
                                        ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)
1618
                        ) {
1619
                                getLayers().moveTo(0, 0);
1620
                        }
1621
                }
1622
        }
1623

    
1624
        public long getDrawVersion() {
1625
                return this.drawVersion;
1626
        }
1627

    
1628
        protected void updateDrawVersion(){
1629
                this.drawVersion++;
1630
        }
1631

    
1632
        public MapContextDrawer getMapContextDrawer() throws ReadException,
1633
            MapContextException {
1634
                if (this.mapContextDrawer == null){
1635
                    if (mapContextDrawerClass == null) {
1636
                this.mapContextDrawer = mapContextManager
1637
                        .createDefaultMapContextDrawerInstance();
1638
            } else {
1639
                this.mapContextDrawer = mapContextManager
1640
                        .createMapContextDrawerInstance(mapContextDrawerClass);
1641
            }
1642
                    this.mapContextDrawer.setMapContext(this);
1643
            this.mapContextDrawer.setViewPort(viewPort);
1644
                }
1645

    
1646
                return this.mapContextDrawer;
1647
        }
1648
        
1649
        public void setMapContextDrawerClass(Class mapContextDrawerClass)
1650
            throws InvalidMapContextDrawerClassException {
1651
                if (! MapContextDrawer.class.isAssignableFrom(mapContextDrawerClass)){
1652
                        throw new InvalidMapContextDrawerClassException(
1653
                    mapContextDrawerClass);
1654
                }
1655
                this.mapContextDrawerClass = mapContextDrawerClass;
1656
                if (this.mapContextDrawer != null){
1657
                        this.mapContextDrawer.dispose();
1658
                        this.mapContextDrawer = null;
1659
                }
1660
        }
1661

    
1662
        public void setMapContextDrawer(MapContextDrawer drawer){
1663
                if (this.mapContextDrawer != null){
1664
                        this.mapContextDrawer.dispose();
1665
                        this.mapContextDrawer = null;
1666
                }
1667
                this.mapContextDrawer = drawer;
1668
                if (this.mapContextDrawer != null){
1669
                        this.mapContextDrawer.setMapContext(this);
1670
                        this.mapContextDrawer.setViewPort(viewPort);
1671
                }
1672
        }
1673

    
1674
}