Statistics
| Revision:

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

History | View | Annotate | Download (54.5 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.RenderingHints;
47
import java.awt.Toolkit;
48
import java.awt.geom.Rectangle2D;
49
import java.awt.image.BufferedImage;
50
import java.util.ArrayList;
51
import java.util.List;
52
import java.util.prefs.Preferences;
53

    
54
import javax.print.attribute.PrintRequestAttributeSet;
55

    
56
import org.cresques.cts.ICoordTrans;
57
import org.cresques.cts.IProjection;
58
import org.cresques.geo.Projected;
59
import org.gvsig.exceptions.BaseException;
60
import org.gvsig.fmap.data.ReadException;
61
import org.gvsig.fmap.data.feature.FeatureStoreNotification;
62
import org.gvsig.fmap.data.feature.visitor.FeaturesVisitor;
63
import org.gvsig.fmap.geom.primitive.DefaultEnvelope;
64
import org.gvsig.fmap.geom.primitive.Envelope;
65
import org.gvsig.fmap.mapcontext.events.ErrorEvent;
66
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
67
import org.gvsig.fmap.mapcontext.events.listeners.ErrorListener;
68
import org.gvsig.fmap.mapcontext.events.listeners.EventBuffer;
69
import org.gvsig.fmap.mapcontext.layers.CancelationException;
70
import org.gvsig.fmap.mapcontext.layers.FLayer;
71
import org.gvsig.fmap.mapcontext.layers.FLayers;
72
import org.gvsig.fmap.mapcontext.layers.GraphicLayer;
73
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
74
import org.gvsig.fmap.mapcontext.layers.LayerCollectionListener;
75
import org.gvsig.fmap.mapcontext.layers.LayerDrawEvent;
76
import org.gvsig.fmap.mapcontext.layers.LayerDrawingListener;
77
import org.gvsig.fmap.mapcontext.layers.LayerPositionEvent;
78
import org.gvsig.fmap.mapcontext.layers.XMLException;
79
import org.gvsig.fmap.mapcontext.layers.operations.Classifiable;
80
import org.gvsig.fmap.mapcontext.layers.operations.SingleLayer;
81
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
82
import org.gvsig.fmap.mapcontext.rendering.strategies.SelectedZoomVisitor;
83
import org.gvsig.util.observer.DefaultObservable;
84
import org.gvsig.util.observer.Observable;
85
import org.gvsig.util.observer.Observer;
86

    
87
import com.iver.utiles.XMLEntity;
88
import com.iver.utiles.swing.threads.Cancellable;
89

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

    
148

    
149
        public static ArrayList AREANAMES=new ArrayList();
150
        public static ArrayList AREAABBR=new ArrayList();
151
        public static ArrayList AREATRANS2METER=new ArrayList();
152

    
153
        public static ArrayList DISTANCENAMES=new ArrayList();
154
        public static ArrayList DISTANCEABBR=new ArrayList();
155
        public static ArrayList DISTANCETRANS2METER=new ArrayList();
156

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

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

    
178

    
179
    }
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, PrintRequestAttributeSet)
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, PrintRequestAttributeSet)
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
         * <p>Color used to represent the selections.</p>
451
         */
452
        private static Color selectionColor = Color.YELLOW;
453
        private DefaultObservable defaultObservable=new DefaultObservable();
454

    
455
        /**
456
         * <p>Gets the color used to represent the selections.</p>
457
         *
458
         * @return color used to represent the selections
459
         */
460
        public static Color getSelectionColor() {
461
                return selectionColor;
462
        }
463

    
464
        /**
465
         * <p>Sets the color used to represent the selections.</p>
466
         *
467
         * @param selectionColor color used to represent the selections
468
         */
469
        public static void setSelectionColor(Color selectionColor) {
470
                MapContext.selectionColor = selectionColor;
471
        }
472

    
473
        /**
474
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
475
         *  without layers.</p>
476
         *
477
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
478
         */
479
        public MapContext(ViewPort vp) {
480
                this.layers = new FLayers();//(this,null);
481
                this.layers.setMapContext(this);
482

    
483
                layerEventListener = new LayerEventListener();
484
                layers.addLayerCollectionListener(layerEventListener);
485
                layers.addLayerCollectionListener(eventBuffer);
486

    
487
                setViewPort(vp);
488

    
489
        }
490

    
491
        /**
492
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
493
         *
494
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
495
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
496
         */
497
        public MapContext(FLayers fLayers, ViewPort vp) {
498
                this.layers = fLayers;
499

    
500
                layerEventListener = new LayerEventListener();
501
                layers.addLayerCollectionListener(layerEventListener);
502
                layers.addLayerCollectionListener(eventBuffer);
503

    
504
                setViewPort(vp);
505
        }
506

    
507
        /**
508
         * <p>Reports to all driver error listeners registered of a bundle of driver exceptions caused in the same map atomic transaction.</p>
509
         *
510
         * @param introductoryText introductory text specified by developer. If <code>null</code>, use ""
511
         * @param driverExceptions list with a bundle of driver exceptions caught during an atomic event
512
         *
513
         * @see #addErrorListener(ErrorListener)
514
         * @see #removeErrorListener(LegendListener)
515
         * @see #callNewErrorEvent(ErrorEvent)
516
         */
517
        public synchronized void reportDriverExceptions(String introductoryText,
518
                                                                                                        List driverExceptions){
519
                for (int i = 0; i < errorListeners.size(); i++) {
520
                        ((ErrorListener) errorListeners.get(i)).
521
                                reportDriverExceptions(introductoryText, driverExceptions);
522
                }
523
        }
524

    
525
        /**
526
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
527
         *
528
         * @param listener the legend listener
529
         *
530
         * @see #removeLayerListener(LegendListener)
531
         * @see #callLegendChanged()
532
         */
533
        public void addLayerListener(LegendListener listener) {
534
                if (!legendListeners.contains(listener)) {
535
                        legendListeners.add(listener);
536
                }
537
        }
538
        // SUGERENCIA DE PABLO
539
        //        public void addLegendListener(LegendListener listener) {
540
        //                if (!legendListeners.contains(listener))
541
        //                        legendListeners.add(listener);
542
        //        }
543

    
544
        /**
545
         * <p>Adds the specified layer drawing listener to catch and handle drawing events from layers of this map.</p>
546
         *
547
         * @param listener the listener to add
548
         *
549
         * @see #removeLayerDrawListener(LayerDrawingListener)
550
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
551
         */
552
        public void addLayerDrawingListener(LayerDrawingListener listener) {
553
                layerDrawingListeners.add(listener);
554
        }
555

    
556
        /**
557
         * <p>Removes the specified layer drawing listener from this map.</p>
558
         *
559
         * @param listener the listener to remove
560
         *
561
         * @see #addLayerDrawingListener(LayerDrawingListener)
562
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
563
         */
564
        public void removeLayerDrawListener(LayerDrawingListener listener) {
565
                layerDrawingListeners.remove(listener);
566
        }
567

    
568
        /**
569
         * <p>Adds the specified error listener to receive error events from this map.</p>
570
         *
571
         * @param listener the listener to add
572
         *
573
         * @see #removeErrorListener(LegendListener)
574
         * @see #callNewErrorEvent(ErrorEvent)
575
         * @see #reportDriverExceptions(String, List)
576
         */
577
        public void addErrorListener(ErrorListener listener) {
578
                errorListeners.add(listener);
579
        }
580

    
581
        /**
582
         * <p>Removes the specified error listener from this map.</p>
583
         *
584
         * @param listener the listener to remove
585
         *
586
         * @see #addErrorListener(ErrorListener)
587
         * @see #callNewErrorEvent(ErrorEvent)
588
         * @see #reportDriverExceptions(String, List)
589
         */
590
        public void removeErrorListener(LegendListener listener) {
591
                legendListeners.remove(listener);
592
        }
593

    
594
        // SUGERENCIA DE PABLO:
595
        //public void removeErrorListener(ErrorListener listener) {
596
        //        errorListeners.remove(listener);
597
        //}
598

    
599
        /**
600
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
601
         * <p>This method must be called only if it's wanted to reflect a legend change.</p>
602
         *
603
         * @see #addLayerListener(LegendListener)
604
         * @see #removeLayerListener(LegendListener)
605
         */
606
        public synchronized void callLegendChanged() {
607
                for (int i = 0; i < legendListeners.size(); i++) {
608
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
609
                }
610
                // getLayers().moveTo(0,0);
611
        }
612

    
613
        /**
614
         * <p>Fires a layer drawing event to all {@link LayerDrawingListener LayerDrawingListener} listeners registered,
615
         *  distinguishing the kind of event.</p>
616
         *
617
         * @param e the event
618
         *
619
         * @see #addLayerDrawingListener(LayerDrawingListener)
620
         * @see #removeLayerDrawListener(LayerDrawingListener)
621
         */
622
        public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
623
                for (int i = 0; i < layerDrawingListeners.size(); i++)
624
                {
625
                        LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
626
                        switch (e.getEventType())
627
                        {
628
                                case LayerDrawEvent.LAYER_BEFORE_DRAW:
629
                                        listener.beforeLayerDraw(e);
630
                                        break;
631
                                case LayerDrawEvent.LAYER_AFTER_DRAW:
632
                                        listener.afterLayerDraw(e);
633
                                        break;
634
                                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
635
                                        listener.beforeGraphicLayerDraw(e);
636
                                        break;
637
                                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
638
                                        listener.afterLayerGraphicDraw(e);
639
                                        break;
640
                        }
641
                }
642
                // getLayers().moveTo(0,0);
643
        }
644

    
645
        /**
646
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
647
         *
648
         * @param e the event with information of the error
649
         *
650
         * @see #addErrorListener(ErrorListener)
651
         * @see #removeErrorListener(LegendListener)
652
         * @see #reportDriverExceptions(String, List)
653
         */
654
        public synchronized void callNewErrorEvent(ErrorEvent e) {
655
                for (int i = 0; i < errorListeners.size(); i++) {
656
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
657
                }
658
                errorListeners.clear();
659
                // getLayers().moveTo(0,0);
660
        }
661

    
662
        /**
663
         * <p>Removes the specified layer listener from this map.</p>
664
         *
665
         * @param listener the listener to remove
666
         *
667
         * @see #addLayerListener(LegendListener)
668
         * @see #callLegendChanged()
669
         */
670
        public void removeLayerListener(LegendListener listener) {
671
                legendListeners.remove(listener);
672
        }
673

    
674
        // SUGERENCIA DE PABLO:
675
        // public void removeLegendListener(LegendListener listener) {
676
        //         legendListeners.remove(listener);
677
        // }
678

    
679
        /**
680
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
681
         *
682
         * @return the hierarchy of nodes of layers stored in this map
683
         */
684
        public FLayers getLayers() {
685
                return layers;
686
        }
687

    
688
        /**
689
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
690
         *
691
         * @param b image with an accessible buffer of image data
692
         */
693
        public void drawLabels(BufferedImage b) {
694
        }
695

    
696
        /**
697
         * @see #redraw()
698
         */
699
        public void invalidate() {
700
                if (getLayers().getLayersCount() > 0) {
701
                        getLayers().moveTo(0, 0);
702
                }
703
        }
704

    
705
        /**
706
         * <p>Prints the layers of this map using the {@link Graphics2D Graphics2D} argument, that usually is
707
         * the {@link Graphics Graphics} of the printer.</p>
708
         *
709
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
710
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
711
         * @param properties a set with the settings to be applied to a whole print job and to all the documents in the print job
712
         *
713
         * @throws ReadDriverException if fails reading with driver.
714
         *
715
         * @see FLayers#print(Graphics2D, ViewPort, Cancellable, double, PrintRequestAttributeSet)
716
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
717
         */
718
        public void print(Graphics2D g, double scale, PrintRequestAttributeSet properties) throws ReadException {
719
                RenderingHints renderHints = new RenderingHints(
720
                                RenderingHints.KEY_ANTIALIASING,
721
                                RenderingHints.VALUE_ANTIALIAS_ON);
722
                renderHints.put(RenderingHints.KEY_RENDERING,
723
                                RenderingHints.VALUE_RENDER_QUALITY);
724
                g.setRenderingHints(renderHints);
725

    
726
                Cancellable cancel = new Cancellable() {
727
                        public boolean isCanceled() {
728
                                return false;
729
                        }
730

    
731
                        public void setCanceled(boolean canceled) {
732
                                // No queremos que se pueda cancelar la impresi?n.
733

    
734
                        }
735
                };
736
                layers.print(g, viewPort, cancel, scale, properties);
737
                tracLayer.draw(null, g, viewPort, cancel, scale);
738
        }
739

    
740
        /**
741
         * <p>Returns a new <code>MapContext</code> instance with the information of the <code>vp</code> argument, and the layers of this map.</p>
742
         *
743
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
744
         *
745
         * @return a new <code>MapContext</code> instance projected by <code>vp</code>
746
         */
747
        public MapContext createNewFMap(ViewPort vp) {
748
                MapContext ret = new MapContext(vp);
749
                ret.layers = this.layers;
750

    
751
                return ret;
752
        }
753

    
754
        /**
755
         * <p>Creates a new independent <code>MapContext</code> instance, that has a clone of the layers and the view port of this one.</p>
756
         * <p>The new map will have the same data source drivers to avoid waste memory, and work faster.</p>
757
         *
758
         * @return the new <code>MapContext</code> instance
759
         *
760
         * @throws XMLException if fails cloning the view port or a layer
761
         *
762
         * @see FLayer#cloneLayer()
763
         * @see ViewPort#cloneViewPort()
764
         */
765
        public MapContext cloneFMap() throws XMLException {
766
                ViewPort vp = getViewPort().cloneViewPort();
767
                FLayers antLayers = getLayers();
768
                MapContext ret = new MapContext(vp);
769
                FLayers aux = new FLayers();//(ret,null);
770
                aux.setMapContext(ret);
771
                for (int i=0; i < antLayers.getLayersCount(); i++)
772
                {
773
                        FLayer lyr = antLayers.getLayer(i);
774
                        try {
775
                                aux.addLayer(lyr.cloneLayer());
776
                        } catch (Exception e) {
777

    
778
                                // TODO Auto-generated catch block
779
                                e.printStackTrace();
780
                                throw new XMLException(e);
781
                        }
782
                }
783
                ret.layers = aux;
784
                return ret;
785

    
786
//                return createFromXML(getXMLEntity());
787

    
788
        }
789

    
790
        /**
791
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
792
         *
793
         * @return the new map
794
         */
795
        public MapContext cloneToDraw() {
796
                ViewPort vp = getViewPort().cloneViewPort();
797
                MapContext mapContext=new MapContext(getLayers(),vp);
798
                return mapContext;
799
        }
800

    
801
        /**
802
         * A?ade la capa que se pasa como par?metro al nodo que se pasa como
803
         * parametro y lanza ProjectionMismatchException si no est?n todas las capas
804
         * de este FMap en la misma proyecci?n. Lanza un ChildNotAllowedException si
805
         * la capa no es un FLayers y no permite hijos
806
         *
807
         * @param vectorial
808
         *            DOCUMENT ME!
809
         */
810

    
811
        /*
812
         * public void addLayer(LayerPath parent, FLayer layer) throws
813
         * ProjectionMismatchException, ChildrenNotAllowedException {
814
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
815
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
816
         */
817

    
818
        /**
819
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
820
         *
821
         * @param vectorial the layer to add
822
         */
823
        public void addToTrackLayer(FLayer vectorial) {
824
        }
825

    
826
        /**
827
         * <p>Returns the scale of the view in the screen.</p>
828
         *
829
         * @return one of this values:
830
         * <ul>
831
         * <li>the scale of the adjusted extent scale of the view in the screen
832
         * <li><code>-1</code> if there is no image
833
         * <li><code>0</code> if there is no extent defined for the image
834
         * </ul>
835
         *
836
         * @see #setScaleView(long)
837
         * @see ViewPort#getAdjustedExtent()
838
         * @see IProjection#getScale(double, double, double, double)
839
         */
840
        public long getScaleView() {
841
                double dpi = getScreenDPI();
842
                IProjection proj = viewPort.getProjection();
843

    
844
                if (viewPort.getImageSize() == null) {
845
                        return -1;
846
                }
847

    
848
                if (viewPort.getAdjustedExtent() == null) {
849
                        return 0;
850
                }
851
                double[] trans2Meter=getDistanceTrans2Meter();
852
                if (proj == null) {
853
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
854
                        return (long) (viewPort.getAdjustedExtent().getLength(0) / w * trans2Meter[getViewPort()
855
                                        .getMapUnits()]);
856
                }
857

    
858
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinimum(0)*trans2Meter[getViewPort().getMapUnits()]),
859
                                (viewPort.getAdjustedExtent().getMaximum(0)*trans2Meter[getViewPort().getMapUnits()]), viewPort.getImageSize()
860
                                                .getWidth(), dpi));
861

    
862
        }
863

    
864
        /**
865
         * <p>Sets the new extent of the view, calculated using the scale argument.</p>
866
         * <p>Doesn't updates the scale if there isn't information about the dimension of the image or the
867
         *  adjusted extent.</p>
868
         *
869
         * @param scale the new scale for the view
870
         *
871
         * @see ViewPort#setProjection(IProjection)
872
         * @see #getScaleView()
873
         */
874
        public void setScaleView(long scale) {
875
                clearAllCachingImageDrawnLayers();
876
                double dpi = getScreenDPI();
877
                if (viewPort.getImageSize() == null) {
878
                        return;
879
                }
880
                IProjection proj = viewPort.getProjection();
881
                if (viewPort.getAdjustedExtent() == null) {
882
                        return;
883
                }
884
                double[] trans2Meter=getDistanceTrans2Meter();
885
                Envelope env=viewPort.getAdjustedExtent();
886
                Rectangle2D r=new Rectangle2D.Double(env.getMinimum(0),env.getMinimum(1),env.getLength(0),env.getLength(1));
887
                Rectangle2D rec=proj.getExtent(r,scale,viewPort.getImageWidth(),viewPort.getImageHeight(),100*getDistanceTrans2Meter()[getViewPort().getMapUnits()],trans2Meter[getViewPort().getDistanceUnits()],dpi);
888
                getViewPort().setEnvelope(new DefaultEnvelope(2,new double[]{rec.getX(),rec.getY()},new double[]{rec.getMaxX(),rec.getMaxY()}));
889
        }
890

    
891
        /**
892
         * <p>Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
893
         * by default as it is defined in the default Toolkit.</p>
894
         *
895
         * @return double with the screen's dpi
896
         */
897
        public static double getScreenDPI() {
898
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
899
                Toolkit kit = Toolkit.getDefaultToolkit();
900
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
901
                return dpi;
902
        }
903

    
904
        /**
905
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
906
         */
907
//        public void setVectorial(VectorialAdapter v) {
908
//        }
909

    
910
        /**
911
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
912
         */
913
        public void process(FeaturesVisitor visitor) {
914
        }
915

    
916
        /**
917
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
918
         */
919
        public void processSelected(FeaturesVisitor visitor) {
920
        }
921

    
922
        /**
923
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
924
         *      VectorialSubSet)
925
         */
926
        public void select(FeaturesVisitor visitor) {
927
        }
928

    
929
        /**
930
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
931
         */
932
        public void selectFromSelection() {
933
        }
934

    
935
        /**
936
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
937
         */
938
        public void createIndex() {
939
        }
940

    
941
        /**
942
         * @see org.cresques.geo.Projected#getProjection()
943
         *
944
         * @see ViewPort#getProjection()
945
         * @see #setProjection(IProjection)
946
         * @see #reProject(ICoordTrans)
947
         */
948
        public IProjection getProjection() {
949
                return getViewPort().getProjection();
950
        }
951

    
952
        /**
953
         * <p>Sets the new projection.</p>
954
         *
955
         * @param proj the new projection
956
         *
957
         * @see #getProjection()
958
         * @see ViewPort#setProjection(IProjection)
959
         * @see #reProject(ICoordTrans)
960
         */
961
        public void setProjection(IProjection proj) {
962
                if (getViewPort() != null) {
963
                        getViewPort().setProjection(proj);
964
                }
965
        }
966

    
967
        /**
968
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
969
         */
970
        public void reProject(ICoordTrans arg0) {
971
                // TODO implementar reprojecci?n (lo que sea eso)
972
        }
973

    
974
        /**
975
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#getSelectionBounds()
976
         *
977
         * @see SelectedZoomVisitor#getSelectBound()
978
         */
979
        public Envelope getSelectionBounds() throws BaseException {
980
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
981

    
982
                layers.accept(visitor);
983

    
984
                return visitor.getSelectBound();
985
        }
986

    
987
        /**
988
         * <p>Draws this map if its {@link ViewPort ViewPort} has an extent defined:<br>
989
         * <ol>
990
         * <li>Selects only the layers that have to be drawn: {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
991
         * <li>Sets quality: antialiasing by text and images, and quality rendering.
992
         * <li>Draws the layers.
993
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
994
         * <li>Draws the graphic layer.
995
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
996
         * <li>Invokes the garbage collector and memory clean.
997
         * </ol></p>
998
         *
999
         * @param image buffer used sometimes instead <code>g</code> to accelerate the draw. For example, if two points are as closed that can't be distinguished, draws only one.
1000
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1001
         * @param cancel shared object that determines if this layer can continue being drawn
1002
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1003
         * @throws ReadDriverException if fails reading with the driver.
1004
         */
1005
        public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
1006
                        double scale) throws ReadException {
1007
                if (viewPort.getExtent() == null) {
1008
                        // System.err.println("viewPort.getExtent() = null");
1009
                        return;
1010
                }
1011
                System.out.println("Viewport despues: " + viewPort.toString());
1012
                /*
1013
                 * if ((viewPort.getImageWidth() <=0) || (viewPort.getImageHeight() <=
1014
                 * 0)) { return; }
1015
                 */
1016

    
1017
                prepareDrawing(image, g, scale);
1018

    
1019
                // M?s c?lidad al texto
1020
                RenderingHints renderHints = new RenderingHints(
1021
                                RenderingHints.KEY_ANTIALIASING,
1022
                                RenderingHints.VALUE_ANTIALIAS_ON);
1023
                renderHints.put(RenderingHints.KEY_RENDERING,
1024
                                RenderingHints.VALUE_RENDER_QUALITY);
1025
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
1026
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1027
                g.setRenderingHints(renderHints);
1028

    
1029
                long t1 = System.currentTimeMillis();
1030
                layers.draw(image, g, viewPort, cancel, scale);
1031

    
1032
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
1033
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1034
                fireLayerDrawingEvent(beforeTracLayerEvent);
1035
                tracLayer.draw(image, g, viewPort, cancel, scale);
1036
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
1037
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1038
                fireLayerDrawingEvent(afterTracLayerEvent);
1039

    
1040
                //layers.setDirty(false);
1041
                long t2 = System.currentTimeMillis();
1042
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
1043
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
1044
                /*
1045
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
1046
                 * GeneralPath(viewPort.getExtent());
1047
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
1048
                 */
1049
                System.gc();
1050
        }
1051

    
1052
        /**
1053
         * <p>Checks out layers that need to be repainted.</p>
1054
         * <p>If one layer node uses a cache with previous image drawn, but hasn't any, or if it's dirty, then,
1055
         *  that layer must be repainted. </p>
1056
         * <p>If one layer node needn't to be repainted, checks out it recursively.</p>
1057
         * <p><i>The cache of "previous image drawn" allows accelerate the repaint process.</i></p>
1058
         *
1059
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1060
         * @see #recursivePrepareDrawing(FLayers, int)
1061
         */
1062
        private void prepareDrawing(BufferedImage image, Graphics2D g, double scale) {
1063

    
1064
                // Primera pasada: si alguna capa necesita repintarse por debajo
1065
                // de la que tiene la cache, TODAS necesitan
1066
                // ser repintadas.
1067
                boolean bNeedRepaint = false;
1068
                boolean bMayExistAceleration = false;
1069
                for (int i = 0; i < layers.getLayersCount(); i++)
1070
                {
1071
                        FLayer lyr = layers.getLayer(i);
1072
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1073
                        {
1074
                                bMayExistAceleration = true;
1075
                        }
1076
                        else
1077
                        {
1078
                                if (lyr.isDirty()) {
1079
                                        bNeedRepaint = true;
1080
                                }
1081
                        }
1082
                }
1083
                if (bMayExistAceleration==false) {
1084
                        bNeedRepaint = true;
1085
                }
1086
                if (bNeedRepaint) {
1087
                        layers.setDirty(true);
1088
                } else {
1089
                        recursivePrepareDrawing(layers);
1090
                }
1091
        }
1092

    
1093
        /**
1094
         * <p>Invoked by {@linkplain MapContext#recursivePrepareDrawing(FLayers, int)}. Sets all previous layer nodes
1095
         *  in the argument as not dirty, what means that don't need to be repainted.</p>
1096
         * <p>Each layer node can decide validate or not it's sub-layers according its implementation.</p>
1097
         * <p>This is useful when it's editing, for accelerate the draw, because the layers of a {@link FLayers FLayers} node
1098
         * are painted according its index in the collection, and each one upper the previous.</p>
1099
         *
1100
         * @param layers a node with layers
1101
         * @param index index of the current layer in the internal list of layers
1102
         *
1103
         * @see #recursivePrepareDrawing(FLayers, int)
1104
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1105
         */
1106
        private void validatePreviousLayers(FLayers layers, int index)
1107
        {
1108
                // TODO: Aqu? quiz?s habr?a que explorar los padres de las capas
1109
                // para marcar y/o asignar la imagen cacheada.
1110
                for (int i = 0; i < index; i++)
1111
                {
1112
                        FLayer lyr = layers.getLayer(i);
1113
                        lyr.setDirty(false);
1114
                }
1115
                // Las de arriba las marcamos como sucias
1116
//                for (int i = index; i < layers.getLayersCount(); i++)
1117
//                {
1118
//                        FLayer lyr = layers.getLayer(i);
1119
//                        lyr.setDirty(true);
1120
//                }
1121
        }
1122

    
1123
        /**
1124
         * <p>Checks out recursively, layers that have a cache with an image of previous layers drawn, and if are dirty
1125
         * notify the previous layers that are valid.</p>
1126
         *
1127
         * @param parent node that contains the layers
1128
         * @param indexInParent the least layer index in the <code>parent</code> argument. This allows reduce the time processing
1129
         *  if is known that the first layers aren't a collection and will (or won't) be validated
1130
         *
1131
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1132
         */
1133
        private void recursivePrepareDrawing(FLayers parent)
1134
        {
1135
                for (int i = 0; i < parent.getLayersCount(); i++)
1136
                {
1137
                        FLayer lyr = parent.getLayer(i);
1138
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1139
                        {
1140
                                // les decimos a las anteriores que est?n validadas (not dirty)
1141
                                // para que no se dibujen.
1142
                                if (lyr.isDirty()) {
1143
                                        validatePreviousLayers(parent, i);
1144
                                }
1145
                        }
1146

    
1147
                        if (lyr instanceof FLayers)
1148
                        {
1149
                                recursivePrepareDrawing((FLayers)lyr);
1150
                        }
1151
                }
1152

    
1153
        }
1154

    
1155
        /**
1156
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1157
         *
1158
         * @param image image used to accelerate the screen draw
1159
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1160
         * @param cancel shared object that determines if this layer can continue being drawn
1161
         * @param scale value that represents the scale
1162
         * @throws ReadDriverException if fails reading with the driver.
1163
         *
1164
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1165
         */
1166
        public void drawGraphics(BufferedImage image, Graphics2D g,
1167
                        Cancellable cancel, double scale) throws ReadException {
1168
                if (viewPort == null) {
1169
                        return;
1170
                }
1171
                tracLayer.draw(image, g, viewPort, cancel, scale);
1172
        }
1173

    
1174
        /**
1175
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1176
         *  the task as cancellable.</p>
1177
         *
1178
         * @param image buffer used sometimes instead <code>g</code> to accelerate the draw. For example, if two points are as closed that can't be distinguished, draws only one.
1179
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1180
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1181
         *
1182
         * @throws ReadDriverException if the driver fails reading.
1183
         *
1184
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1185
         */
1186
        public void draw(BufferedImage image, Graphics2D g, double scale)
1187
                        throws ReadException {
1188
                layers.setDirty(true);
1189
                draw(image, g, new Cancellable() {
1190
                        /**
1191
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1192
                         */
1193
                        public boolean isCanceled() {
1194
                                return false;
1195
                        }
1196

    
1197
                        public void setCanceled(boolean canceled) {
1198
                                // TODO Auto-generated method stub
1199

    
1200
                        }
1201
                }, scale);
1202
        }
1203

    
1204
        /**
1205
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1206
         *
1207
         * @return the view port
1208
         *
1209
         * @see #setViewPort(ViewPort)
1210
         */
1211
        public ViewPort getViewPort() {
1212
                return viewPort;
1213
        }
1214

    
1215
        /**
1216
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1217
         *  of this map.</p>
1218
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1219
         *  adds the new one.</p>
1220
         *
1221
         * @param viewPort the viewPort
1222
         *
1223
         * @see #getViewPort()
1224
         */
1225
        public void setViewPort(ViewPort viewPort) {
1226
                if (this.viewPort != null) {
1227
                        this.viewPort.removeViewPortListener(eventBuffer);
1228
                }
1229

    
1230
                this.viewPort = viewPort;
1231
                if (viewPort != null) {
1232
                        viewPort.addViewPortListener(eventBuffer);
1233
                }
1234
        }
1235

    
1236
        /**
1237
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1238
         *
1239
         * @param extent the extent of the new zoom
1240
         */
1241
        public void zoomToEnvelope(Envelope extent) {
1242
                if (extent!=null) {
1243
                        getViewPort().setEnvelope(extent);
1244
                }
1245
        }
1246

    
1247
        /**
1248
         * <p>Returns the union of all extents of all layers of this map.</p>
1249
         *
1250
         * @return full extent of layers of this map
1251
         * @throws ReadDriverException if the driver fails reading.
1252
         *
1253
         * @see FLayers#getFullEnvelope()
1254
         */
1255
        public Envelope getFullEnvelope() throws ReadException {
1256
                return layers.getFullEnvelope();
1257
        }
1258

    
1259
        /**
1260
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1261
         * <ul>
1262
         * <li>XML entity of the internal {@link ViewPort ViewPort}.
1263
         * <li>XML entity of the internal {@link FLayers FLayers}.
1264
         * </ul>
1265
         *
1266
         * @return XMLEntity the XML entity
1267
         * @throws XMLException if there is any error creating the XML from the map.
1268
         *
1269
         * @see #createFromXML(XMLEntity)
1270
         * @see ViewPort#getXMLEntity()
1271
         * @see FLayers#getXMLEntity()
1272
         */
1273
        public XMLEntity getXMLEntity() throws XMLException {
1274
                XMLEntity xml = new XMLEntity();
1275
                xml.putProperty("className", this.getClass().getName());
1276
                xml.addChild(viewPort.getXMLEntity());
1277
                xml.addChild(layers.getXMLEntity());
1278

    
1279
                return xml;
1280
        }
1281

    
1282
        /**
1283
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1284
         *  with the data of the {@link ViewPort ViewPort} and
1285
         *  {@link FLayers FLayers}.</p>
1286
         *
1287
         * @param xml an XML entity
1288
         *
1289
         * @return the new <code>MapContext</code> instance
1290
         *
1291
         * @throws XMLException if there is any error creating the map from the XML.
1292
         *
1293
         * @see #getXMLEntity()
1294
         * @see ViewPort#createFromXML(XMLEntity)
1295
         * @see FLayers#setXMLEntity(XMLEntity)
1296
         */
1297
        public static MapContext createFromXML(XMLEntity xml) throws XMLException {
1298
                ViewPort vp = ViewPort.createFromXML(xml.getChild(0));
1299
                MapContext fmap = new MapContext(vp);
1300
                fmap.layers.setXMLEntity(xml.getChild(1));
1301
                fmap.layers.setName("root layer");
1302
                return fmap;
1303
        }
1304

    
1305
        /**
1306
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1307
         *
1308
         * @param listener the new listener
1309
         *
1310
         * @return <code>true</code> if has added the listener successfully
1311
         *
1312
         * @see #removeAtomicEventListener(AtomicEventListener)
1313
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1314
         */
1315
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1316
                return eventBuffer.addAtomicEventListener(listener);
1317
        }
1318

    
1319
        /**
1320
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1321
         *
1322
         * @param listener the listener to remove
1323
         *
1324
     * @return <tt>true</tt> if the list contained the specified element
1325
         *
1326
         * @see #addAtomicEventListener(AtomicEventListener)
1327
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1328
         */
1329
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1330
                return eventBuffer.removeAtomicEventListener(listener);
1331
        }
1332

    
1333
        /**
1334
         * @see EventBuffer#beginAtomicEvent()
1335
         *
1336
         * @see #endAtomicEvent()
1337
         */
1338
        public void beginAtomicEvent() {
1339
                eventBuffer.beginAtomicEvent();
1340
        }
1341

    
1342
        /**
1343
         * @see EventBuffer#endAtomicEvent()
1344
         *
1345
         * @see #beginAtomicEvent()
1346
         */
1347
        public void endAtomicEvent() {
1348
                eventBuffer.endAtomicEvent();
1349
        }
1350

    
1351
        /**
1352
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1353
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1354
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1355
         *
1356
         * @author Fernando Gonz?lez Cort?s
1357
         */
1358
        public class LayerEventListener implements LayerCollectionListener {
1359
                /*
1360
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1361
                 */
1362
                public void layerAdded(LayerCollectionEvent e) {
1363
                        // Si es la primera capa, fijamos su extent al ViewPort
1364
                        // if (getLayers().getLayersCount() == 1) {
1365
                        if (getViewPort().getExtent() == null) {
1366
                                FLayer lyr = e.getAffectedLayer();
1367
                                if (lyr.isAvailable()) {
1368
                                        try {
1369
                                                getViewPort().setEnvelope(lyr.getFullEnvelope());
1370
                                        } catch (ReadException e1) {
1371
                                                e1.printStackTrace();
1372
                                        }
1373
                                }
1374
                        }
1375

    
1376
                        // Registramos al FMap como listener del legend de las capas
1377
                        FLayer lyr = e.getAffectedLayer();
1378
                        selectionListener(lyr);
1379
                        if (lyr instanceof SingleLayer){
1380
                                ((SingleLayer)lyr).getDataStore().addObserver(MapContext.this);
1381
                        }
1382
                }
1383

    
1384
                /**
1385
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1386
                 *
1387
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1388
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1389
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1390
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1391
                 *
1392
                 * @param the layer or layers
1393
                 */
1394
                private void selectionListener(FLayer lyr){
1395
                        lyr.addLayerListener(eventBuffer);
1396

    
1397
                        if (lyr instanceof Classifiable) {
1398
                                Classifiable c = (Classifiable) lyr;
1399
                                c.addLegendListener(eventBuffer);
1400
                        }
1401

    
1402
//                        if (lyr instanceof AlphanumericData) {
1403
//                                Selectable s=null;
1404
//                                try {
1405
//                                        s = ((AlphanumericData) lyr).getRecordset();
1406
//                                        if (s!=null) {
1407
//                                                s.addSelectionListener(eventBuffer);
1408
//                                        }
1409
//                                } catch (ReadException e1) {
1410
//                                        e1.printStackTrace();
1411
//                                }
1412
//
1413
//                        }
1414
                        if (lyr instanceof FLayers){
1415
                                FLayers lyrs=(FLayers)lyr;
1416
                                for(int i=0;i<lyrs.getLayersCount();i++){
1417
                                        selectionListener(lyrs.getLayer(i));
1418
                                }
1419
                        }
1420

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

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

    
1434
                        lyr.removeLayerListener(eventBuffer);
1435

    
1436
                        if (lyr instanceof Classifiable) {
1437
                                Classifiable c = (Classifiable) lyr;
1438
                                c.removeLegendListener(eventBuffer);
1439
                        }
1440

    
1441
                        if (lyr instanceof SingleLayer) {
1442
                                ((SingleLayer) lyr).getDataStore().deleteObserver(
1443
                                                MapContext.this);
1444
                        }
1445

    
1446
//                        if (lyr instanceof FLyrVect ) {
1447
//                                Selectable s = (Selectable) lyr;
1448
//                                s.addSelectionListener(eventBuffer);
1449
//                        }
1450
                }
1451

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

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

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

    
1473

    
1474
                /*
1475
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1476
                 */
1477
                public void visibilityChanged(LayerCollectionEvent e)
1478
                                throws CancelationException {
1479
                }
1480
        }
1481

    
1482
        /**
1483
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1484
         *  collection of layers argument.</p>
1485
         *
1486
         * @param a collection of layers
1487
         */
1488
        public void addAsCollectionListener(FLayers layers2) {
1489
                layers2.addLayerCollectionListener(layerEventListener);
1490
        }
1491

    
1492
        /**
1493
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1494
         *
1495
         * @return the graphic layer of this map
1496
         *
1497
         * @see #setGraphicsLayer(GraphicLayer)
1498
         */
1499
        public GraphicLayer getGraphicsLayer() {
1500
                return tracLayer;
1501
        }
1502

    
1503
        /**
1504
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1505
         *
1506
         * @param graphicLayer the new graphic layer
1507
         *
1508
         * @see #getGraphicsLayer()
1509
         */
1510
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1511
                tracLayer = graphicLayer;
1512
        }
1513

    
1514
        /**
1515
         * <p>Indicates whether some other object is "equal to" this map.</p>
1516
         * <p>Returns <code>true</code> if success one of this options:
1517
         * <ul>
1518
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1519
         * <li>Both maps have the same layers.
1520
         * <li>Both maps have the same number of layers and with the same name.
1521
         * </ul>
1522
         * </p>
1523
         *
1524
         * @param obj the reference object with which to compare.
1525
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1526
         *
1527
         * @see Object#equals(Object)
1528
         */
1529
        public boolean equals(Object arg0) {
1530
                MapContext map = (MapContext) arg0;
1531
                if (super.equals(arg0)) {
1532
                        return true;
1533
                }
1534
                if (getLayers() == map.getLayers()) {
1535
                        return true;
1536
                }
1537
                boolean isEqual = true;
1538
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1539
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1540

    
1541
                                if (!getLayers().getLayer(i).getName().equals(
1542
                                                map.getLayers().getLayer(i).getName())) {
1543
                                        isEqual = false;
1544
                                }
1545

    
1546
                        }
1547
                } else {
1548
                        isEqual = false;
1549
                }
1550
                return isEqual;
1551
        }
1552

    
1553
        /**
1554
         * <p>Registers the message of an error associated to this map.</p>
1555
         *
1556
         * @param stringProperty the error message
1557
         *
1558
         * @see #getLayersError()
1559
         * @see #clearErrors()
1560
         */
1561
        public void addLayerError(String stringProperty) {
1562
                layersError.add(stringProperty);
1563
        }
1564

    
1565
        /**
1566
         * <p>Gets the list with all error messages registered to this map.</p>
1567
         *
1568
         * @return the list of errors registered to this map
1569
         *
1570
         * @see #addLayerError(String)
1571
         * @see #clearErrors()
1572
         */
1573
        public ArrayList getLayersError() {
1574
                return layersError;
1575
        }
1576

    
1577
        /**
1578
         * <p>Removes all error messages associated to this map.</p>
1579
         *
1580
         * @see #addLayerError(String)
1581
         * @see #getLayersError()
1582
         */
1583
        public void clearErrors() {
1584
                layersError.clear();
1585
        }
1586

    
1587
        /**
1588
         * <p>Removes from this map, all caching images of drawn layers, registered.</p>
1589
         *
1590
         * @see #clearCachingImageDrawnLayers(FLayers)
1591
         */
1592
        public void clearAllCachingImageDrawnLayers() {
1593
                clearCachingImageDrawnLayers(this.layers);
1594
        }
1595

    
1596
        /**
1597
         * <p>Removes from the layer collection argument, all caching images of drawn layers, registered.</p>
1598
         *
1599
         * @param layers a layer collection
1600
         *
1601
         * @see #clearAllCachingImageDrawnLayers()
1602
         */
1603
        private void clearCachingImageDrawnLayers(FLayers layers){
1604
                int i;
1605
                FLayer layer;
1606
                for (i=0;i< layers.getLayersCount();i++){
1607
                        layer = layers.getLayer(i);
1608
                        if (layer instanceof FLayers) {
1609
                                clearCachingImageDrawnLayers((FLayers)layer);
1610
                        } else {
1611
                                layer.setCacheImageDrawnLayers(null);
1612
                        }
1613
                }
1614
        }
1615

    
1616
        /**
1617
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1618
         *
1619
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1620
         * @return the new layer node
1621
         */
1622
        public FLayers getNewGroupLayer(FLayers parent) {
1623
                FLayers group1 = new FLayers();//(this,parent);
1624
                group1.setMapContext(this);
1625
                group1.setParentLayer(parent);
1626
            return group1;
1627
        }
1628

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

    
1642
                }
1643

    
1644
        }
1645
}