Statistics
| Revision:

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

History | View | Annotate | Download (53.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.data.ReadException;
60
import org.gvsig.data.vectorial.visitor.FeaturesVisitor;
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.layers.CancelationException;
66
import org.gvsig.fmap.mapcontext.layers.FLayer;
67
import org.gvsig.fmap.mapcontext.layers.FLayers;
68
import org.gvsig.fmap.mapcontext.layers.GraphicLayer;
69
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
70
import org.gvsig.fmap.mapcontext.layers.LayerCollectionListener;
71
import org.gvsig.fmap.mapcontext.layers.LayerDrawEvent;
72
import org.gvsig.fmap.mapcontext.layers.LayerDrawingListener;
73
import org.gvsig.fmap.mapcontext.layers.LayerPositionEvent;
74
import org.gvsig.fmap.mapcontext.layers.XMLException;
75
import org.gvsig.fmap.mapcontext.layers.operations.AlphanumericData;
76
import org.gvsig.fmap.mapcontext.layers.operations.Classifiable;
77
import org.gvsig.fmap.mapcontext.layers.operations.Selectable;
78
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
79
import org.gvsig.fmap.mapcontext.rendering.strategies.SelectedZoomVisitor;
80
import org.gvsig.fmap.mapcontrol.MapControl;
81

    
82
import com.iver.utiles.XMLEntity;
83
import com.iver.utiles.swing.threads.Cancellable;
84

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

    
143

    
144
        public static ArrayList AREANAMES=new ArrayList();
145
        public static ArrayList AREAABBR=new ArrayList();
146
        public static ArrayList AREATRANS2METER=new ArrayList();
147

    
148
        public static ArrayList DISTANCENAMES=new ArrayList();
149
        public static ArrayList DISTANCEABBR=new ArrayList();
150
        public static ArrayList DISTANCETRANS2METER=new ArrayList();
151

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

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

    
173

    
174
    }
175

    
176

    
177

    
178
        public static void addAreaUnit(String name, String abbr,boolean isLinear,double trans2meter){
179
                AREANAMES.add(name);
180
                String pow="";
181
                if (isLinear)
182
                        pow=String.valueOf((char)178);
183
                AREAABBR.add(abbr+pow);
184
                AREATRANS2METER.add(new Double(trans2meter));
185
        }
186
        public static String[] getAreaNames(){
187
                return (String[])AREANAMES.toArray(new String[0]);
188
        }
189
        public static String[] getAreaAbbr(){
190
                return (String[])AREAABBR.toArray(new String[0]);
191
        }
192
        public static double[] getAreaTrans2Meter(){
193
                int size=AREATRANS2METER.size();
194
                double[] trans2meters=new double[size];
195
                for (int i = 0; i < size; i++) {
196
                        trans2meters[i]=((Double)AREATRANS2METER.get(i)).doubleValue();
197
                }
198
                return trans2meters;
199
        }
200
        public static String getOfLinear(int i) {
201
                if (((String)AREAABBR.get(i)).toLowerCase().endsWith(String.valueOf((char)178))){
202
                        return String.valueOf((char)178);
203
                }
204
                return "";
205
        }
206
        public static void addDistanceUnit(String name, String abbr,double trans2meter){
207
                DISTANCENAMES.add(name);
208
                DISTANCEABBR.add(abbr);
209
                DISTANCETRANS2METER.add(new Double(trans2meter));
210
        }
211
        public static String[] getDistanceNames(){
212
                return (String[])DISTANCENAMES.toArray(new String[0]);
213
        }
214
        public static String[] getDistanceAbbr(){
215
                return (String[])DISTANCEABBR.toArray(new String[0]);
216
        }
217
        public static double[] getDistanceTrans2Meter(){
218
                int size=DISTANCETRANS2METER.size();
219
                double[] trans2meters=new double[size];
220
                for (int i = 0; i < size; i++) {
221
                        trans2meters[i]=((Double)DISTANCETRANS2METER.get(i)).doubleValue();
222
                }
223
                return trans2meters;
224
        }
225
        public static int getDistancePosition(String s){
226
                for (int i = 0; i < DISTANCENAMES.size(); i++) {
227
                        if (DISTANCENAMES.get(i).equals(s)){
228
                                return i;
229
                        }
230
                }
231
                return 0;
232
        }
233

    
234
        /**
235
         * <p>Defines the value which a unit of a distance measurement must be divided to obtain its equivalent <b>in centimeters</b>.</p>
236
         *
237
         * <p><b><i>Conversion values of distance measurements:</i></b>
238
         * <ul>
239
         *  <li><code>MapContext.CHANGE[0]</code>: kilometer
240
         *  <li><code>MapContext.CHANGE[1]</code>: meter
241
         *  <li><code>MapContext.CHANGE[2]</code>: centimeter
242
         *  <li><code>MapContext.CHANGE[3]</code>: millimeter
243
         *  <li><code>MapContext.CHANGE[4]</code>: international statute mile
244
         *  <li><code>MapContext.CHANGE[5]</code>: yard
245
         *  <li><code>MapContext.CHANGE[6]</code>: foot
246
         *  <li><code>MapContext.CHANGE[7]</code>: inch
247
         *  <li><code>MapContext.CHANGE[8]</code>: grade
248
         * </ul>
249
         *
250
         * <p><h3>Examples:</h3>
251
         * <pre>1 international statute mile / MapContext.CHANGE[4] = X centimeters</pre>
252
         * <pre>1 kilometer / MapContext.CHANGE[0] = X centimeters</pre>
253
         * <pre>1 grade / MapContext.CHANGE[8] = X centimeters</pre>
254
         * </p>
255
         *
256
         * <p><h3>Grade conversion value: <code>MapContext.CHANGE[8]</code></h3>
257
         * The value of <code>MapContext.CHANGE[8]</code> represents the centimeters of a straight line between two
258
         *  points on the Earth surface that are 1 grade far each other of the center of the Earth. This value has been calculated using
259
         *  a radius approximated of R<sub>Earth</sub>=6.37846082678100774672e6 meters, according these equations:
260
         * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
261
         * <pre>MapContext.CHANGE[8] = 1 / D</pre>
262
         * <h4>Explanation:</h4>
263
         * We get an isosceles triangle with the center of the Earth and the 2 points on the surface. This triangle can be divided into
264
         * 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
265
         * is the hypotenuse. Then we apply trigonometry and get the distance <i>D</i> between both points on the Earth surface.</p>
266
         * <p>Now we only must invert that value to obtain <code>MapContext.CHANGE[8]</code>.</p>
267
         * @deprecated use getDistanceTrans2Meter() * 100
268
         */
269
        public static final double[] CHANGE = { 100000, 100, 1, 0.1, 160934.4,
270
                        91.44, 30.48, 2.54, 1/8.983152841195214E-4 };
271

    
272
        /* Do not alter the order and the values of this array, if you need append values.*/
273
        /**
274
         * <p>Gets the name of all distance measurements supported by <code>MapContext</code>.</p>
275
         */
276
//        public static final String[] NAMES= {
277
//                Messages.getString("Kilometros"),
278
//                Messages.getString("Metros"),
279
//                Messages.getString("Centimetros"),
280
//                Messages.getString("Milimetros"),
281
//                Messages.getString("Millas"),
282
//                Messages.getString("Yardas"),
283
//                Messages.getString("Pies"),
284
//                Messages.getString("Pulgadas"),
285
//                Messages.getString("Grados"),
286
//        };
287

    
288
        public static final int EQUALS = 0;
289

    
290
        public static final int DISJOINT = 1;
291

    
292
        public static final int INTERSECTS = 2;
293

    
294
        public static final int TOUCHES = 3;
295

    
296
        public static final int CROSSES = 4;
297

    
298
        public static final int WITHIN = 5;
299

    
300
        public static final int CONTAINS = 6;
301

    
302
        public static final int OVERLAPS = 7;
303

    
304
        /**
305
         * A hierarchy of {@link FLayers FLayers} nodes.
306
         *
307
         * @see #getLayers()
308
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
309
         */
310
        protected FLayers layers;
311

    
312
        /**
313
         * A layer with graphical items: geometries and symbols.
314
         *
315
         * @see #getGraphicsLayer()
316
         * @see #setGraphicsLayer(GraphicLayer)
317
         * @see #drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
318
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
319
         */
320
        private GraphicLayer tracLayer = new GraphicLayer();
321

    
322
        /**
323
         * Information for draw layers in a view.
324
         *
325
         * @see #getViewPort()
326
         * @see #setViewPort(ViewPort)
327
         */
328
        private ViewPort viewPort;
329

    
330
        // private ArrayList invalidationListeners = new ArrayList();
331

    
332
        /**
333
         * Array list with all {@link LegendListener LegendListener} registered to this map.
334
         *
335
         * @see #addLayerListener(LegendListener)
336
         * @see #removeLayerListener(LegendListener)
337
         * @see #callLegendChanged()
338
         */
339
        private ArrayList legendListeners = new ArrayList();
340

    
341
        /**
342
         * Array list with all {@link LayerDrawingListener LayerDrawingListener} registered to this map.
343
         *
344
         * @see #addLayerDrawingListener(LayerDrawingListener)
345
         * @see #removeLayerDrawListener(LayerDrawingListener)
346
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
347
         */
348
        private ArrayList layerDrawingListeners = new ArrayList();
349

    
350
        /**
351
         * <p>Buffer that is used to store and eject events produced on this map:
352
         * <ul>
353
         *  <li>Layer collection events.
354
         *  <li>View port events.
355
         *  <li>Atomic events.
356
         *  <li>Layer events.
357
         *  <li>Legend events on a {@link Classificable Classificable} layer.
358
         *  <li>Selection events on an {@link AlphanumericData AlphanumericData} data layer.
359
         * </ul>
360
         * </p>
361
         *
362
         * @see #addAtomicEventListener(AtomicEventListener)
363
         * @see #removeAtomicEventListener(AtomicEventListener)
364
         * @see #beginAtomicEvent()
365
         * @see #endAtomicEvent()
366
         */
367
        private EventBuffer eventBuffer = new EventBuffer();
368

    
369
        /**
370
         * Event listener for the collection of layers of this map.
371
         */
372
        private LayerEventListener layerEventListener = null;
373

    
374
        /**
375
         * List with information of all errors produced on all layers.
376
         *
377
         * @see #addLayerError(String)
378
         * @see #getLayersError()
379
         * @see #clearErrors()
380
         */
381
        private ArrayList layersError = new ArrayList();
382

    
383
        /**
384
         * Array list with all {@link ErrorListener ErrorListener} registered to this map.
385
         *
386
         * @see #addErrorListener(ErrorListener)
387
         * @see #removeErrorListener(LegendListener)
388
         * @see #callNewErrorEvent(ErrorEvent)
389
         * @see #reportDriverExceptions(String, List)
390
         */
391
        private ArrayList errorListeners = new ArrayList();
392

    
393

    
394

    
395
        // public static ResourceBundle myResourceBundle =
396
        // ResourceBundle.getBundle("FMap");
397

    
398
        /**
399
         * <p>Default <i>zoom in</i> factor.</p>
400
         * <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
401
         * area but with the items bigger.</p>
402
         */
403
        public static double ZOOMINFACTOR=2;
404

    
405
        /**
406
         * <p>Default <i>zoom out</i> factor.</p>
407
         * <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
408
         * area but with the items smaller.</p>
409
         */
410
        public static double ZOOMOUTFACTOR=0.5;
411

    
412
        /**
413
         * <p>Color used to represent the selections.</p>
414
         */
415
        private static Color selectionColor = Color.YELLOW;
416

    
417

    
418
        /**
419
         * <p>Gets the color used to represent the selections.</p>
420
         *
421
         * @return color used to represent the selections
422
         */
423
        public static Color getSelectionColor() {
424
                return selectionColor;
425
        }
426

    
427
        /**
428
         * <p>Sets the color used to represent the selections.</p>
429
         *
430
         * @param selectionColor color used to represent the selections
431
         */
432
        public static void setSelectionColor(Color selectionColor) {
433
                MapContext.selectionColor = selectionColor;
434
        }
435

    
436
        /**
437
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
438
         *  without layers.</p>
439
         *
440
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
441
         */
442
        public MapContext(ViewPort vp) {
443
                this.layers = new FLayers();//(this,null);
444
                this.layers.setMapContext(this);
445

    
446
                layerEventListener = new LayerEventListener();
447
                layers.addLayerCollectionListener(layerEventListener);
448
                layers.addLayerCollectionListener(eventBuffer);
449

    
450
                setViewPort(vp);
451

    
452
        }
453

    
454
        /**
455
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
456
         *
457
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
458
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
459
         */
460
        public MapContext(FLayers fLayers, ViewPort vp) {
461
                this.layers = fLayers;
462

    
463
                layerEventListener = new LayerEventListener();
464
                layers.addLayerCollectionListener(layerEventListener);
465
                layers.addLayerCollectionListener(eventBuffer);
466

    
467
                setViewPort(vp);
468
        }
469

    
470
        /**
471
         * <p>Reports to all driver error listeners registered of a bundle of driver exceptions caused in the same map atomic transaction.</p>
472
         *
473
         * @param introductoryText introductory text specified by developer. If <code>null</code>, use ""
474
         * @param driverExceptions list with a bundle of driver exceptions caught during an atomic event
475
         *
476
         * @see #addErrorListener(ErrorListener)
477
         * @see #removeErrorListener(LegendListener)
478
         * @see #callNewErrorEvent(ErrorEvent)
479
         */
480
        public synchronized void reportDriverExceptions(String introductoryText,
481
                                                                                                        List driverExceptions){
482
                for (int i = 0; i < errorListeners.size(); i++) {
483
                        ((ErrorListener) errorListeners.get(i)).
484
                                reportDriverExceptions(introductoryText, driverExceptions);
485
                }
486
        }
487

    
488
        /**
489
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
490
         *
491
         * @param listener the legend listener
492
         *
493
         * @see #removeLayerListener(LegendListener)
494
         * @see #callLegendChanged()
495
         */
496
        public void addLayerListener(LegendListener listener) {
497
                if (!legendListeners.contains(listener))
498
                        legendListeners.add(listener);
499
        }
500
        // SUGERENCIA DE PABLO
501
        //        public void addLegendListener(LegendListener listener) {
502
        //                if (!legendListeners.contains(listener))
503
        //                        legendListeners.add(listener);
504
        //        }
505

    
506
        /**
507
         * <p>Adds the specified layer drawing listener to catch and handle drawing events from layers of this map.</p>
508
         *
509
         * @param listener the listener to add
510
         *
511
         * @see #removeLayerDrawListener(LayerDrawingListener)
512
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
513
         */
514
        public void addLayerDrawingListener(LayerDrawingListener listener) {
515
                layerDrawingListeners.add(listener);
516
        }
517

    
518
        /**
519
         * <p>Removes the specified layer drawing listener from this map.</p>
520
         *
521
         * @param listener the listener to remove
522
         *
523
         * @see #addLayerDrawingListener(LayerDrawingListener)
524
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
525
         */
526
        public void removeLayerDrawListener(LayerDrawingListener listener) {
527
                layerDrawingListeners.remove(listener);
528
        }
529

    
530
        /**
531
         * <p>Adds the specified error listener to receive error events from this map.</p>
532
         *
533
         * @param listener the listener to add
534
         *
535
         * @see #removeErrorListener(LegendListener)
536
         * @see #callNewErrorEvent(ErrorEvent)
537
         * @see #reportDriverExceptions(String, List)
538
         */
539
        public void addErrorListener(ErrorListener listener) {
540
                errorListeners.add(listener);
541
        }
542

    
543
        /**
544
         * <p>Removes the specified error listener from this map.</p>
545
         *
546
         * @param listener the listener to remove
547
         *
548
         * @see #addErrorListener(ErrorListener)
549
         * @see #callNewErrorEvent(ErrorEvent)
550
         * @see #reportDriverExceptions(String, List)
551
         */
552
        public void removeErrorListener(LegendListener listener) {
553
                legendListeners.remove(listener);
554
        }
555

    
556
        // SUGERENCIA DE PABLO:
557
        //public void removeErrorListener(ErrorListener listener) {
558
        //        errorListeners.remove(listener);
559
        //}
560

    
561
        /**
562
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
563
         * <p>This method must be called only if it's wanted to reflect a legend change.</p>
564
         *
565
         * @see #addLayerListener(LegendListener)
566
         * @see #removeLayerListener(LegendListener)
567
         */
568
        public synchronized void callLegendChanged() {
569
                for (int i = 0; i < legendListeners.size(); i++) {
570
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
571
                }
572
                // getLayers().moveTo(0,0);
573
        }
574

    
575
        /**
576
         * <p>Fires a layer drawing event to all {@link LayerDrawingListener LayerDrawingListener} listeners registered,
577
         *  distinguishing the kind of event.</p>
578
         *
579
         * @param e the event
580
         *
581
         * @see #addLayerDrawingListener(LayerDrawingListener)
582
         * @see #removeLayerDrawListener(LayerDrawingListener)
583
         */
584
        public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
585
                for (int i = 0; i < layerDrawingListeners.size(); i++)
586
                {
587
                        LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
588
                        switch (e.getEventType())
589
                        {
590
                                case LayerDrawEvent.LAYER_BEFORE_DRAW:
591
                                        listener.beforeLayerDraw(e);
592
                                        break;
593
                                case LayerDrawEvent.LAYER_AFTER_DRAW:
594
                                        listener.afterLayerDraw(e);
595
                                        break;
596
                                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
597
                                        listener.beforeGraphicLayerDraw(e);
598
                                        break;
599
                                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
600
                                        listener.afterLayerGraphicDraw(e);
601
                                        break;
602
                        }
603
                }
604
                // getLayers().moveTo(0,0);
605
        }
606

    
607
        /**
608
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
609
         *
610
         * @param e the event with information of the error
611
         *
612
         * @see #addErrorListener(ErrorListener)
613
         * @see #removeErrorListener(LegendListener)
614
         * @see #reportDriverExceptions(String, List)
615
         */
616
        public synchronized void callNewErrorEvent(ErrorEvent e) {
617
                for (int i = 0; i < errorListeners.size(); i++) {
618
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
619
                }
620
                errorListeners.clear();
621
                // getLayers().moveTo(0,0);
622
        }
623

    
624
        /**
625
         * <p>Removes the specified layer listener from this map.</p>
626
         *
627
         * @param listener the listener to remove
628
         *
629
         * @see #addLayerListener(LegendListener)
630
         * @see #callLegendChanged()
631
         */
632
        public void removeLayerListener(LegendListener listener) {
633
                legendListeners.remove(listener);
634
        }
635

    
636
        // SUGERENCIA DE PABLO:
637
        // public void removeLegendListener(LegendListener listener) {
638
        //         legendListeners.remove(listener);
639
        // }
640

    
641
        /**
642
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
643
         *
644
         * @return the hierarchy of nodes of layers stored in this map
645
         */
646
        public FLayers getLayers() {
647
                return layers;
648
        }
649

    
650
        /**
651
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
652
         *
653
         * @param b image with an accessible buffer of image data
654
         */
655
        public void drawLabels(BufferedImage b) {
656
        }
657

    
658
        /**
659
         * @see #redraw()
660
         */
661
        public void invalidate() {
662
                if (getLayers().getLayersCount() > 0)
663
                        getLayers().moveTo(0, 0);
664
        }
665

    
666
        /**
667
         * <p>Prints the layers of this map using the {@link Graphics2D Graphics2D} argument, that usually is
668
         * the {@link Graphics Graphics} of the printer.</p>
669
         *
670
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
671
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
672
         * @param properties a set with the settings to be applied to a whole print job and to all the documents in the print job
673
         *
674
         * @throws ReadDriverException if fails reading with driver.
675
         *
676
         * @see FLayers#print(Graphics2D, ViewPort, Cancellable, double, PrintRequestAttributeSet)
677
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
678
         */
679
        public void print(Graphics2D g, double scale, PrintRequestAttributeSet properties) throws ReadException {
680
                RenderingHints renderHints = new RenderingHints(
681
                                RenderingHints.KEY_ANTIALIASING,
682
                                RenderingHints.VALUE_ANTIALIAS_ON);
683
                renderHints.put(RenderingHints.KEY_RENDERING,
684
                                RenderingHints.VALUE_RENDER_QUALITY);
685
                g.setRenderingHints(renderHints);
686

    
687
                Cancellable cancel = new Cancellable() {
688
                        public boolean isCanceled() {
689
                                return false;
690
                        }
691

    
692
                        public void setCanceled(boolean canceled) {
693
                                // No queremos que se pueda cancelar la impresi?n.
694

    
695
                        }
696
                };
697
                layers.print(g, viewPort, cancel, scale, properties);
698
                tracLayer.draw(null, g, viewPort, cancel, scale);
699
        }
700

    
701
        /**
702
         * <p>Returns a new <code>MapContext</code> instance with the information of the <code>vp</code> argument, and the layers of this map.</p>
703
         *
704
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
705
         *
706
         * @return a new <code>MapContext</code> instance projected by <code>vp</code>
707
         */
708
        public MapContext createNewFMap(ViewPort vp) {
709
                MapContext ret = new MapContext(vp);
710
                ret.layers = this.layers;
711

    
712
                return ret;
713
        }
714

    
715
        /**
716
         * <p>Creates a new independent <code>MapContext</code> instance, that has a clone of the layers and the view port of this one.</p>
717
         * <p>The new map will have the same data source drivers to avoid waste memory, and work faster.</p>
718
         *
719
         * @return the new <code>MapContext</code> instance
720
         *
721
         * @throws XMLException if fails cloning the view port or a layer
722
         *
723
         * @see FLayer#cloneLayer()
724
         * @see ViewPort#cloneViewPort()
725
         */
726
        public MapContext cloneFMap() throws XMLException {
727
                ViewPort vp = getViewPort().cloneViewPort();
728
                FLayers antLayers = getLayers();
729
                MapContext ret = new MapContext(vp);
730
                FLayers aux = new FLayers();//(ret,null);
731
                aux.setMapContext(ret);
732
                for (int i=0; i < antLayers.getLayersCount(); i++)
733
                {
734
                        FLayer lyr = antLayers.getLayer(i);
735
                        try {
736
                                aux.addLayer(lyr.cloneLayer());
737
                        } catch (Exception e) {
738

    
739
                                // TODO Auto-generated catch block
740
                                e.printStackTrace();
741
                                throw new XMLException(e);
742
                        }
743
                }
744
                ret.layers = aux;
745
                return ret;
746

    
747
//                return createFromXML(getXMLEntity());
748

    
749
        }
750

    
751
        /**
752
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
753
         *
754
         * @return the new map
755
         */
756
        public MapContext cloneToDraw() {
757
                ViewPort vp = getViewPort().cloneViewPort();
758
                MapContext mapContext=new MapContext(getLayers(),vp);
759
                return mapContext;
760
        }
761

    
762
        /**
763
         * A?ade la capa que se pasa como par?metro al nodo que se pasa como
764
         * parametro y lanza ProjectionMismatchException si no est?n todas las capas
765
         * de este FMap en la misma proyecci?n. Lanza un ChildNotAllowedException si
766
         * la capa no es un FLayers y no permite hijos
767
         *
768
         * @param vectorial
769
         *            DOCUMENT ME!
770
         */
771

    
772
        /*
773
         * public void addLayer(LayerPath parent, FLayer layer) throws
774
         * ProjectionMismatchException, ChildrenNotAllowedException {
775
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
776
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
777
         */
778

    
779
        /**
780
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
781
         *
782
         * @param vectorial the layer to add
783
         */
784
        public void addToTrackLayer(FLayer vectorial) {
785
        }
786

    
787
        /**
788
         * <p>Returns the scale of the view in the screen.</p>
789
         *
790
         * @return one of this values:
791
         * <ul>
792
         * <li>the scale of the adjusted extent scale of the view in the screen
793
         * <li><code>-1</code> if there is no image
794
         * <li><code>0</code> if there is no extent defined for the image
795
         * </ul>
796
         *
797
         * @see #setScaleView(long)
798
         * @see ViewPort#getAdjustedExtent()
799
         * @see IProjection#getScale(double, double, double, double)
800
         */
801
        public long getScaleView() {
802
                double dpi = getScreenDPI();
803
                IProjection proj = viewPort.getProjection();
804

    
805
                if (viewPort.getImageSize() == null)
806
                        return -1;
807

    
808
                if (viewPort.getAdjustedExtent() == null) {
809
                        return 0;
810
                }
811
                double[] trans2Meter=getDistanceTrans2Meter();
812
                if (proj == null) {
813
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
814
                        return (long) (viewPort.getAdjustedExtent().getWidth() / w * trans2Meter[getViewPort()
815
                                        .getMapUnits()]);
816
                }
817

    
818
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*trans2Meter[getViewPort().getMapUnits()]),
819
                                (viewPort.getAdjustedExtent().getMaxX()*trans2Meter[getViewPort().getMapUnits()]), viewPort.getImageSize()
820
                                                .getWidth(), dpi));
821

    
822
        }
823

    
824
        /**
825
         * <p>Sets the new extent of the view, calculated using the scale argument.</p>
826
         * <p>Doesn't updates the scale if there isn't information about the dimension of the image or the
827
         *  adjusted extent.</p>
828
         *
829
         * @param scale the new scale for the view
830
         *
831
         * @see ViewPort#setProjection(IProjection)
832
         * @see #getScaleView()
833
         */
834
        public void setScaleView(long scale) {
835
                clearAllCachingImageDrawnLayers();
836
                double dpi = getScreenDPI();
837
                if (viewPort.getImageSize() == null)
838
                        return;
839
                IProjection proj = viewPort.getProjection();
840
                if (viewPort.getAdjustedExtent() == null) {
841
                        return;
842
                }
843
                double[] trans2Meter=getDistanceTrans2Meter();
844
                Rectangle2D rec=proj.getExtent(viewPort.getAdjustedExtent(),scale,viewPort.getImageWidth(),viewPort.getImageHeight(),100*getDistanceTrans2Meter()[getViewPort().getMapUnits()],trans2Meter[getViewPort().getDistanceUnits()],dpi);
845
                getViewPort().setExtent(rec);
846
        }
847

    
848
        /**
849
         * <p>Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
850
         * by default as it is defined in the default Toolkit.</p>
851
         *
852
         * @return double with the screen's dpi
853
         */
854
        public static double getScreenDPI() {
855
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
856
                Toolkit kit = Toolkit.getDefaultToolkit();
857
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
858
                return dpi;
859
        }
860

    
861
        /**
862
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
863
         */
864
//        public void setVectorial(VectorialAdapter v) {
865
//        }
866

    
867
        /**
868
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
869
         */
870
        public void process(FeaturesVisitor visitor) {
871
        }
872

    
873
        /**
874
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
875
         */
876
        public void processSelected(FeaturesVisitor visitor) {
877
        }
878

    
879
        /**
880
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
881
         *      VectorialSubSet)
882
         */
883
        public void select(FeaturesVisitor visitor) {
884
        }
885

    
886
        /**
887
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
888
         */
889
        public void selectFromSelection() {
890
        }
891

    
892
        /**
893
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
894
         */
895
        public void createIndex() {
896
        }
897

    
898
        /**
899
         * @see org.cresques.geo.Projected#getProjection()
900
         *
901
         * @see ViewPort#getProjection()
902
         * @see #setProjection(IProjection)
903
         * @see #reProject(ICoordTrans)
904
         */
905
        public IProjection getProjection() {
906
                return getViewPort().getProjection();
907
        }
908

    
909
        /**
910
         * <p>Sets the new projection.</p>
911
         *
912
         * @param proj the new projection
913
         *
914
         * @see #getProjection()
915
         * @see ViewPort#setProjection(IProjection)
916
         * @see #reProject(ICoordTrans)
917
         */
918
        public void setProjection(IProjection proj) {
919
                if (getViewPort() != null) {
920
                        getViewPort().setProjection(proj);
921
                }
922
        }
923

    
924
        /**
925
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
926
         */
927
        public void reProject(ICoordTrans arg0) {
928
                // TODO implementar reprojecci?n (lo que sea eso)
929
        }
930

    
931
        /**
932
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByPoint(java.awt.geom.Point2D,
933
         *      double)
934
         */
935
        /*
936
         * public void selectByPoint(Point2D p, double tolerance) throws
937
         * DriverException { Point2D mapPoint = viewPort.toMapPoint((int) p.getX(),
938
         * (int) p.getY()); SelectByPointVisitor visitor = new
939
         * SelectByPointVisitor(); visitor.setQueriedPoint(mapPoint);
940
         * visitor.setTolerance(getViewPort().toMapDistance(3));
941
         *
942
         * try { layers.process(visitor); } catch (VisitException e) { throw new
943
         * RuntimeException("No se espera que SelectByPointVisitor lance esta
944
         * excepci?n", e); } }
945
         */
946

    
947
        /**
948
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByRect(java.awt.geom.Rectangle2D)
949
         */
950
        /*
951
         * public void selectByRect(Rectangle2D rect) throws DriverException {
952
         * FLayer[] actives = layers.getActives(); for (int i=0; i < actives.length;
953
         * i++) { if (actives[i] instanceof FLyrVect) { FLyrVect lyrVect =
954
         * (FLyrVect) actives[i]; FBitSet oldBitSet = lyrVect.getSelection();
955
         * FBitSet newBitSet = lyrVect.queryByRect(rect); newBitSet.xor(oldBitSet);
956
         * lyrVect.setSelection(newBitSet); } }
957
         *  }
958
         */
959

    
960
        /**
961
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
962
         *      int)
963
         */
964
//        public void selectByShape(IGeometry g, int relationship) {
965
//        }
966

    
967
        /**
968
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#queryByPoint(Point2D,
969
         *      double)
970
         */
971
//        public Record[] queryByPoint(Point2D p, double tolerance) {
972
//                return null;
973
//        }
974

    
975
        /**
976
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#queryByRect(java.awt.geom.Rectangle2D)
977
         */
978
//        public Record[] queryByRect(Rectangle2D rect) {
979
//                return null;
980
//        }
981

    
982
        /**
983
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#queryByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
984
         *      int)
985
         */
986
//        public Record[] queryByShape(IGeometry g, int relationship) {
987
//                return null;
988
//        }
989

    
990
        /**
991
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#getSelectionBounds()
992
         *
993
         * @see SelectedZoomVisitor#getSelectBound()
994
         */
995
        public Rectangle2D getSelectionBounds() {
996
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
997

    
998
                try {
999
                        layers.process(visitor);
1000
                } catch (ReadException e1) {
1001
                        throw new RuntimeException(
1002
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
1003
                                        e1);
1004
                }
1005

    
1006
                return visitor.getSelectBound();
1007
        }
1008

    
1009
        /**
1010
         * <p>Draws this map if its {@link ViewPort ViewPort} has an extent defined:<br>
1011
         * <ol>
1012
         * <li>Selects only the layers that have to be drawn: {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
1013
         * <li>Sets quality: antialiasing by text and images, and quality rendering.
1014
         * <li>Draws the layers.
1015
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
1016
         * <li>Draws the graphic layer.
1017
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
1018
         * <li>Invokes the garbage collector and memory clean.
1019
         * </ol></p>
1020
         *
1021
         * @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.
1022
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1023
         * @param cancel shared object that determines if this layer can continue being drawn
1024
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1025
         * @throws ReadDriverException if fails reading with the driver.
1026
         */
1027
        public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
1028
                        double scale) throws ReadException {
1029
                if (viewPort.getExtent() == null) {
1030
                        // System.err.println("viewPort.getExtent() = null");
1031
                        return;
1032
                }
1033
                System.out.println("Viewport despues: " + viewPort.toString());
1034
                /*
1035
                 * if ((viewPort.getImageWidth() <=0) || (viewPort.getImageHeight() <=
1036
                 * 0)) { return; }
1037
                 */
1038

    
1039
                prepareDrawing(image, g, scale);
1040

    
1041
                // M?s c?lidad al texto
1042
                RenderingHints renderHints = new RenderingHints(
1043
                                RenderingHints.KEY_ANTIALIASING,
1044
                                RenderingHints.VALUE_ANTIALIAS_ON);
1045
                renderHints.put(RenderingHints.KEY_RENDERING,
1046
                                RenderingHints.VALUE_RENDER_QUALITY);
1047
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
1048
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1049
                g.setRenderingHints(renderHints);
1050

    
1051
                long t1 = System.currentTimeMillis();
1052
                layers.draw(image, g, viewPort, cancel, scale);
1053

    
1054
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
1055
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1056
                fireLayerDrawingEvent(beforeTracLayerEvent);
1057
                tracLayer.draw(image, g, viewPort, cancel, scale);
1058
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
1059
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1060
                fireLayerDrawingEvent(afterTracLayerEvent);
1061

    
1062
                //layers.setDirty(false);
1063
                long t2 = System.currentTimeMillis();
1064
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
1065
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
1066
                /*
1067
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
1068
                 * GeneralPath(viewPort.getExtent());
1069
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
1070
                 */
1071
                System.gc();
1072
        }
1073

    
1074
        /**
1075
         * <p>Checks out layers that need to be repainted.</p>
1076
         * <p>If one layer node uses a cache with previous image drawn, but hasn't any, or if it's dirty, then,
1077
         *  that layer must be repainted. </p>
1078
         * <p>If one layer node needn't to be repainted, checks out it recursively.</p>
1079
         * <p><i>The cache of "previous image drawn" allows accelerate the repaint process.</i></p>
1080
         *
1081
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1082
         * @see #recursivePrepareDrawing(FLayers, int)
1083
         */
1084
        private void prepareDrawing(BufferedImage image, Graphics2D g, double scale) {
1085

    
1086
                // Primera pasada: si alguna capa necesita repintarse por debajo
1087
                // de la que tiene la cache, TODAS necesitan
1088
                // ser repintadas.
1089
                boolean bNeedRepaint = false;
1090
                boolean bMayExistAceleration = false;
1091
                for (int i = 0; i < layers.getLayersCount(); i++)
1092
                {
1093
                        FLayer lyr = layers.getLayer(i);
1094
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1095
                        {
1096
                                bMayExistAceleration = true;
1097
                        }
1098
                        else
1099
                        {
1100
                                if (lyr.isDirty())
1101
                                        bNeedRepaint = true;
1102
                        }
1103
                }
1104
                if (bMayExistAceleration==false)
1105
                        bNeedRepaint = true;
1106
                if (bNeedRepaint)
1107
                        layers.setDirty(true);
1108
                else
1109
                        recursivePrepareDrawing(layers);
1110
        }
1111

    
1112
        /**
1113
         * <p>Invoked by {@linkplain MapContext#recursivePrepareDrawing(FLayers, int)}. Sets all previous layer nodes
1114
         *  in the argument as not dirty, what means that don't need to be repainted.</p>
1115
         * <p>Each layer node can decide validate or not it's sub-layers according its implementation.</p>
1116
         * <p>This is useful when it's editing, for accelerate the draw, because the layers of a {@link FLayers FLayers} node
1117
         * are painted according its index in the collection, and each one upper the previous.</p>
1118
         *
1119
         * @param layers a node with layers
1120
         * @param index index of the current layer in the internal list of layers
1121
         *
1122
         * @see #recursivePrepareDrawing(FLayers, int)
1123
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1124
         */
1125
        private void validatePreviousLayers(FLayers layers, int index)
1126
        {
1127
                // TODO: Aqu? quiz?s habr?a que explorar los padres de las capas
1128
                // para marcar y/o asignar la imagen cacheada.
1129
                for (int i = 0; i < index; i++)
1130
                {
1131
                        FLayer lyr = layers.getLayer(i);
1132
                        lyr.setDirty(false);
1133
                }
1134
                // Las de arriba las marcamos como sucias
1135
//                for (int i = index; i < layers.getLayersCount(); i++)
1136
//                {
1137
//                        FLayer lyr = layers.getLayer(i);
1138
//                        lyr.setDirty(true);
1139
//                }
1140
        }
1141

    
1142
        /**
1143
         * <p>Checks out recursively, layers that have a cache with an image of previous layers drawn, and if are dirty
1144
         * notify the previous layers that are valid.</p>
1145
         *
1146
         * @param parent node that contains the layers
1147
         * @param indexInParent the least layer index in the <code>parent</code> argument. This allows reduce the time processing
1148
         *  if is known that the first layers aren't a collection and will (or won't) be validated
1149
         *
1150
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1151
         */
1152
        private void recursivePrepareDrawing(FLayers parent)
1153
        {
1154
                for (int i = 0; i < parent.getLayersCount(); i++)
1155
                {
1156
                        FLayer lyr = parent.getLayer(i);
1157
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1158
                        {
1159
                                // les decimos a las anteriores que est?n validadas (not dirty)
1160
                                // para que no se dibujen.
1161
                                if (lyr.isDirty())
1162
                                        validatePreviousLayers(parent, i);
1163
                        }
1164

    
1165
                        if (lyr instanceof FLayers)
1166
                        {
1167
                                recursivePrepareDrawing((FLayers)lyr);
1168
                        }
1169
                }
1170

    
1171
        }
1172

    
1173
        /**
1174
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1175
         *
1176
         * @param image image used to accelerate the screen draw
1177
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1178
         * @param cancel shared object that determines if this layer can continue being drawn
1179
         * @param scale value that represents the scale
1180
         * @throws ReadDriverException if fails reading with the driver.
1181
         *
1182
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1183
         */
1184
        public void drawGraphics(BufferedImage image, Graphics2D g,
1185
                        Cancellable cancel, double scale) throws ReadException {
1186
                if (viewPort == null)
1187
                        return;
1188
                tracLayer.draw(image, g, viewPort, cancel, scale);
1189
        }
1190

    
1191
        /**
1192
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1193
         *  the task as cancellable.</p>
1194
         *
1195
         * @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.
1196
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1197
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1198
         *
1199
         * @throws ReadDriverException if the driver fails reading.
1200
         *
1201
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1202
         */
1203
        public void draw(BufferedImage image, Graphics2D g, double scale)
1204
                        throws ReadException {
1205
                layers.setDirty(true);
1206
                draw(image, g, new Cancellable() {
1207
                        /**
1208
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1209
                         */
1210
                        public boolean isCanceled() {
1211
                                return false;
1212
                        }
1213

    
1214
                        public void setCanceled(boolean canceled) {
1215
                                // TODO Auto-generated method stub
1216

    
1217
                        }
1218
                }, scale);
1219
        }
1220

    
1221
        /**
1222
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1223
         *
1224
         * @return the view port
1225
         *
1226
         * @see #setViewPort(ViewPort)
1227
         */
1228
        public ViewPort getViewPort() {
1229
                return viewPort;
1230
        }
1231

    
1232
        /**
1233
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1234
         *  of this map.</p>
1235
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1236
         *  adds the new one.</p>
1237
         *
1238
         * @param viewPort the viewPort
1239
         *
1240
         * @see #getViewPort()
1241
         */
1242
        public void setViewPort(ViewPort viewPort) {
1243
                if (this.viewPort != null) {
1244
                        this.viewPort.removeViewPortListener(eventBuffer);
1245
                }
1246

    
1247
                this.viewPort = viewPort;
1248
                if (viewPort != null)
1249
                        viewPort.addViewPortListener(eventBuffer);
1250
        }
1251

    
1252
        /**
1253
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1254
         *
1255
         * @param extent the extent of the new zoom
1256
         */
1257
        public void zoomToExtent(Rectangle2D extent) {
1258
                if (extent!=null)
1259
                        getViewPort().setExtent(extent);
1260
        }
1261

    
1262
        /**
1263
         * <p>Returns the union of all extents of all layers of this map.</p>
1264
         *
1265
         * @return full extent of layers of this map
1266
         * @throws ReadDriverException if the driver fails reading.
1267
         *
1268
         * @see FLayers#getFullExtent()
1269
         */
1270
        public Rectangle2D getFullExtent() throws ReadException {
1271
                return layers.getFullExtent();
1272
        }
1273

    
1274
        /**
1275
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1276
         * <ul>
1277
         * <li>XML entity of the internal {@link ViewPort ViewPort}.
1278
         * <li>XML entity of the internal {@link FLayers FLayers}.
1279
         * </ul>
1280
         *
1281
         * @return XMLEntity the XML entity
1282
         * @throws XMLException if there is any error creating the XML from the map.
1283
         *
1284
         * @see #createFromXML(XMLEntity)
1285
         * @see #createFromXML03(XMLEntity)
1286
         * @see ViewPort#getXMLEntity()
1287
         * @see FLayers#getXMLEntity()
1288
         */
1289
        public XMLEntity getXMLEntity() throws XMLException {
1290
                XMLEntity xml = new XMLEntity();
1291
                xml.putProperty("className", this.getClass().getName());
1292
                xml.addChild(viewPort.getXMLEntity());
1293
                xml.addChild(layers.getXMLEntity());
1294

    
1295
                return xml;
1296
        }
1297

    
1298
        /**
1299
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1300
         *  the data of the {@link ViewPort ViewPort} and
1301
         *  {@link FLayers FLayers}.</p>
1302
         *
1303
         * @param xml an XML entity
1304
         *
1305
         * @return the new <code>MapContext</code> instance
1306
         *
1307
         * @throws XMLException if there is any error creating the map from the XML.
1308
         *
1309
         * @see #getXMLEntity()
1310
         * @see #createFromXML(XMLEntity)
1311
         * @see ViewPort#createFromXML03(XMLEntity)
1312
         * @see FLayers#setXMLEntity03(XMLEntity)
1313
         */
1314
        public static MapContext createFromXML03(XMLEntity xml) throws XMLException {
1315
                ViewPort vp = ViewPort.createFromXML03(xml.getChild(0));
1316
                MapContext fmap = new MapContext(vp);
1317
                fmap.layers.setXMLEntity03(xml.getChild(1));
1318

    
1319
                return fmap;
1320
        }
1321

    
1322
        /**
1323
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1324
         *  with the data of the {@link ViewPort ViewPort} and
1325
         *  {@link FLayers FLayers}.</p>
1326
         *
1327
         * @param xml an XML entity
1328
         *
1329
         * @return the new <code>MapContext</code> instance
1330
         *
1331
         * @throws XMLException if there is any error creating the map from the XML.
1332
         *
1333
         * @see #getXMLEntity()
1334
         * @see #createFromXML03(XMLEntity)
1335
         * @see ViewPort#createFromXML(XMLEntity)
1336
         * @see FLayers#setXMLEntity(XMLEntity)
1337
         */
1338
        public static MapContext createFromXML(XMLEntity xml) throws XMLException {
1339
                ViewPort vp = ViewPort.createFromXML(xml.getChild(0));
1340
                MapContext fmap = new MapContext(vp);
1341
                fmap.layers.setXMLEntity(xml.getChild(1));
1342
                fmap.layers.setName("root layer");
1343
                return fmap;
1344
        }
1345

    
1346
        /**
1347
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1348
         *
1349
         * @param listener the new listener
1350
         *
1351
         * @return <code>true</code> if has added the listener successfully
1352
         *
1353
         * @see #removeAtomicEventListener(AtomicEventListener)
1354
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1355
         */
1356
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1357
                return eventBuffer.addAtomicEventListener(listener);
1358
        }
1359

    
1360
        /**
1361
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1362
         *
1363
         * @param listener the listener to remove
1364
         *
1365
     * @return <tt>true</tt> if the list contained the specified element
1366
         *
1367
         * @see #addAtomicEventListener(AtomicEventListener)
1368
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1369
         */
1370
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1371
                return eventBuffer.removeAtomicEventListener(listener);
1372
        }
1373

    
1374
        /**
1375
         * @see EventBuffer#beginAtomicEvent()
1376
         *
1377
         * @see #endAtomicEvent()
1378
         */
1379
        public void beginAtomicEvent() {
1380
                eventBuffer.beginAtomicEvent();
1381
        }
1382

    
1383
        /**
1384
         * @see EventBuffer#endAtomicEvent()
1385
         *
1386
         * @see #beginAtomicEvent()
1387
         */
1388
        public void endAtomicEvent() {
1389
                eventBuffer.endAtomicEvent();
1390
        }
1391

    
1392
        /**
1393
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1394
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1395
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1396
         *
1397
         * @author Fernando Gonz?lez Cort?s
1398
         */
1399
        public class LayerEventListener implements LayerCollectionListener {
1400
                /*
1401
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1402
                 */
1403
                public void layerAdded(LayerCollectionEvent e) {
1404
                        // Si es la primera capa, fijamos su extent al ViewPort
1405
                        // if (getLayers().getLayersCount() == 1) {
1406
                        if (getViewPort().getExtent() == null) {
1407
                                FLayer lyr = e.getAffectedLayer();
1408
                                if (lyr.isAvailable()) {
1409
                                        try {
1410
                                                getViewPort().setExtent(lyr.getFullExtent());
1411
                                        } catch (ReadException e1) {
1412
                                                e1.printStackTrace();
1413
                                        }
1414
                                }
1415
                        }
1416

    
1417
                        // Registramos al FMap como listener del legend de las capas
1418
                        FLayer lyr = e.getAffectedLayer();
1419
                        selectionListener(lyr);
1420
                }
1421

    
1422
                /**
1423
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1424
                 *
1425
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1426
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1427
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1428
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1429
                 *
1430
                 * @param the layer or layers
1431
                 */
1432
                private void selectionListener(FLayer lyr){
1433
                        lyr.addLayerListener(eventBuffer);
1434

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

    
1440
//                        if (lyr instanceof AlphanumericData) {
1441
//                                Selectable s=null;
1442
//                                try {
1443
//                                        s = ((AlphanumericData) lyr).getRecordset();
1444
//                                        if (s!=null) {
1445
//                                                s.addSelectionListener(eventBuffer);
1446
//                                        }
1447
//                                } catch (ReadException e1) {
1448
//                                        e1.printStackTrace();
1449
//                                }
1450
//
1451
//                        }
1452
                        if (lyr instanceof FLayers){
1453
                                FLayers lyrs=(FLayers)lyr;
1454
                                for(int i=0;i<lyrs.getLayersCount();i++){
1455
                                        selectionListener(lyrs.getLayer(i));
1456
                                }
1457
                        }
1458

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

    
1466
                /*
1467
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1468
                 */
1469
                public void layerRemoved(LayerCollectionEvent e) {
1470
                        FLayer lyr = e.getAffectedLayer();
1471

    
1472
                        lyr.removeLayerListener(eventBuffer);
1473

    
1474
                        if (lyr instanceof Classifiable) {
1475
                                Classifiable c = (Classifiable) lyr;
1476
                                c.removeLegendListener(eventBuffer);
1477
                        }
1478

    
1479
                        if (lyr instanceof Selectable) {
1480
                                Selectable s = (Selectable) lyr;
1481
                                s.addSelectionListener(eventBuffer);
1482
                        }
1483
                }
1484

    
1485
                /*
1486
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1487
                 */
1488
                public void layerAdding(LayerCollectionEvent e)
1489
                                throws CancelationException {
1490
                }
1491

    
1492
                /*
1493
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1494
                 */
1495
                public void layerMoving(LayerPositionEvent e)
1496
                                throws CancelationException {
1497
                }
1498

    
1499
                /*
1500
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1501
                 */
1502
                public void layerRemoving(LayerCollectionEvent e)
1503
                                throws CancelationException {
1504
                }
1505

    
1506

    
1507
                /*
1508
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1509
                 */
1510
                public void visibilityChanged(LayerCollectionEvent e)
1511
                                throws CancelationException {
1512
                }
1513
        }
1514

    
1515
        /**
1516
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1517
         *  collection of layers argument.</p>
1518
         *
1519
         * @param a collection of layers
1520
         */
1521
        public void addAsCollectionListener(FLayers layers2) {
1522
                layers2.addLayerCollectionListener(layerEventListener);
1523
        }
1524

    
1525
        /**
1526
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1527
         *
1528
         * @return the graphic layer of this map
1529
         *
1530
         * @see #setGraphicsLayer(GraphicLayer)
1531
         */
1532
        public GraphicLayer getGraphicsLayer() {
1533
                return tracLayer;
1534
        }
1535

    
1536
        /**
1537
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1538
         *
1539
         * @param graphicLayer the new graphic layer
1540
         *
1541
         * @see #getGraphicsLayer()
1542
         */
1543
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1544
                tracLayer = graphicLayer;
1545
        }
1546

    
1547
        /**
1548
         * <p>Indicates whether some other object is "equal to" this map.</p>
1549
         * <p>Returns <code>true</code> if success one of this options:
1550
         * <ul>
1551
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1552
         * <li>Both maps have the same layers.
1553
         * <li>Both maps have the same number of layers and with the same name.
1554
         * </ul>
1555
         * </p>
1556
         *
1557
         * @param obj the reference object with which to compare.
1558
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1559
         *
1560
         * @see Object#equals(Object)
1561
         */
1562
        public boolean equals(Object arg0) {
1563
                MapContext map = (MapContext) arg0;
1564
                if (super.equals(arg0))
1565
                        return true;
1566
                if (getLayers() == map.getLayers())
1567
                        return true;
1568
                boolean isEqual = true;
1569
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1570
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1571

    
1572
                                if (!getLayers().getLayer(i).getName().equals(
1573
                                                map.getLayers().getLayer(i).getName())) {
1574
                                        isEqual = false;
1575
                                }
1576

    
1577
                        }
1578
                } else {
1579
                        isEqual = false;
1580
                }
1581
                return isEqual;
1582
        }
1583

    
1584
        /**
1585
         * <p>Registers the message of an error associated to this map.</p>
1586
         *
1587
         * @param stringProperty the error message
1588
         *
1589
         * @see #getLayersError()
1590
         * @see #clearErrors()
1591
         */
1592
        public void addLayerError(String stringProperty) {
1593
                layersError.add(stringProperty);
1594
        }
1595

    
1596
        /**
1597
         * <p>Gets the list with all error messages registered to this map.</p>
1598
         *
1599
         * @return the list of errors registered to this map
1600
         *
1601
         * @see #addLayerError(String)
1602
         * @see #clearErrors()
1603
         */
1604
        public ArrayList getLayersError() {
1605
                return layersError;
1606
        }
1607

    
1608
        /**
1609
         * <p>Removes all error messages associated to this map.</p>
1610
         *
1611
         * @see #addLayerError(String)
1612
         * @see #getLayersError()
1613
         */
1614
        public void clearErrors() {
1615
                layersError.clear();
1616
        }
1617

    
1618
        /**
1619
         * <p>Removes from this map, all caching images of drawn layers, registered.</p>
1620
         *
1621
         * @see #clearCachingImageDrawnLayers(FLayers)
1622
         */
1623
        public void clearAllCachingImageDrawnLayers() {
1624
                clearCachingImageDrawnLayers(this.layers);
1625
        }
1626

    
1627
        /**
1628
         * <p>Removes from the layer collection argument, all caching images of drawn layers, registered.</p>
1629
         *
1630
         * @param layers a layer collection
1631
         *
1632
         * @see #clearAllCachingImageDrawnLayers()
1633
         */
1634
        private void clearCachingImageDrawnLayers(FLayers layers){
1635
                int i;
1636
                FLayer layer;
1637
                for (i=0;i< layers.getLayersCount();i++){
1638
                        layer = layers.getLayer(i);
1639
                        if (layer instanceof FLayers) {
1640
                                clearCachingImageDrawnLayers((FLayers)layer);
1641
                        } else {
1642
                                layer.setCacheImageDrawnLayers(null);
1643
                        }
1644
                }
1645
        }
1646

    
1647
        /**
1648
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1649
         *
1650
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1651
         * @return the new layer node
1652
         */
1653
        public FLayers getNewGroupLayer(FLayers parent) {
1654
                FLayers group1 = new FLayers();//(this,parent);
1655
                group1.setMapContext(this);
1656
                group1.setParentLayer(parent);
1657
            return group1;
1658
        }
1659

    
1660
}