Statistics
| Revision:

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

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

    
55
import javax.print.attribute.PrintRequestAttributeSet;
56

    
57
import org.cresques.cts.ICoordTrans;
58
import org.cresques.cts.IProjection;
59
import org.cresques.geo.Projected;
60
import org.gvsig.fmap.core.geometries.IGeometry;
61
import org.gvsig.fmap.drivers.exceptions.VisitorException;
62
import org.gvsig.fmap.drivers.reading.adapters.VectorialAdapter;
63
import org.gvsig.fmap.mapcontext.events.ErrorEvent;
64
import org.gvsig.fmap.mapcontext.events.listeners.AtomicEventListener;
65
import org.gvsig.fmap.mapcontext.events.listeners.ErrorListener;
66
import org.gvsig.fmap.mapcontext.events.listeners.EventBuffer;
67
import org.gvsig.fmap.mapcontext.layers.CancelationException;
68
import org.gvsig.fmap.mapcontext.layers.FLayer;
69
import org.gvsig.fmap.mapcontext.layers.FLayers;
70
import org.gvsig.fmap.mapcontext.layers.GraphicLayer;
71
import org.gvsig.fmap.mapcontext.layers.LayerCollectionEvent;
72
import org.gvsig.fmap.mapcontext.layers.LayerCollectionListener;
73
import org.gvsig.fmap.mapcontext.layers.LayerDrawEvent;
74
import org.gvsig.fmap.mapcontext.layers.LayerDrawingListener;
75
import org.gvsig.fmap.mapcontext.layers.LayerPositionEvent;
76
import org.gvsig.fmap.mapcontext.layers.XMLException;
77
import org.gvsig.fmap.mapcontext.layers.operations.AlphanumericData;
78
import org.gvsig.fmap.mapcontext.layers.operations.Classifiable;
79
import org.gvsig.fmap.mapcontext.layers.operations.Selectable;
80
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
81
import org.gvsig.fmap.mapcontext.rendering.strategies.FeatureVisitor;
82
import org.gvsig.fmap.mapcontext.rendering.strategies.SelectedZoomVisitor;
83
import org.gvsig.fmap.mapcontrol.MapControl;
84

    
85
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
86
import com.hardcode.gdbms.engine.data.driver.DriverException;
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 {
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

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

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

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

    
293
        public static final int EQUALS = 0;
294

    
295
        public static final int DISJOINT = 1;
296

    
297
        public static final int INTERSECTS = 2;
298

    
299
        public static final int TOUCHES = 3;
300

    
301
        public static final int CROSSES = 4;
302

    
303
        public static final int WITHIN = 5;
304

    
305
        public static final int CONTAINS = 6;
306

    
307
        public static final int OVERLAPS = 7;
308

    
309
        /**
310
         * A hierarchy of {@link FLayers FLayers} nodes.
311
         *
312
         * @see #getLayers()
313
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
314
         */
315
        protected FLayers layers;
316

    
317
        /**
318
         * A layer with graphical items: geometries and symbols.
319
         *
320
         * @see #getGraphicsLayer()
321
         * @see #setGraphicsLayer(GraphicLayer)
322
         * @see #drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
323
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
324
         */
325
        private GraphicLayer tracLayer = new GraphicLayer();
326

    
327
        /**
328
         * Information for draw layers in a view.
329
         *
330
         * @see #getViewPort()
331
         * @see #setViewPort(ViewPort)
332
         */
333
        private ViewPort viewPort;
334

    
335
        // private ArrayList invalidationListeners = new ArrayList();
336

    
337
        /**
338
         * Array list with all {@link LegendListener LegendListener} registered to this map.
339
         *
340
         * @see #addLayerListener(LegendListener)
341
         * @see #removeLayerListener(LegendListener)
342
         * @see #callLegendChanged()
343
         */
344
        private ArrayList legendListeners = new ArrayList();
345

    
346
        /**
347
         * Array list with all {@link LayerDrawingListener LayerDrawingListener} registered to this map.
348
         *
349
         * @see #addLayerDrawingListener(LayerDrawingListener)
350
         * @see #removeLayerDrawListener(LayerDrawingListener)
351
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
352
         */
353
        private ArrayList layerDrawingListeners = new ArrayList();
354

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

    
374
        /**
375
         * Event listener for the collection of layers of this map.
376
         */
377
        private LayerEventListener layerEventListener = null;
378

    
379
        /**
380
         * List with information of all errors produced on all layers.
381
         *
382
         * @see #addLayerError(String)
383
         * @see #getLayersError()
384
         * @see #clearErrors()
385
         */
386
        private ArrayList layersError = new ArrayList();
387

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

    
398

    
399

    
400
        // public static ResourceBundle myResourceBundle =
401
        // ResourceBundle.getBundle("FMap");
402

    
403
        /**
404
         * <p>Default <i>zoom in</i> factor.</p>
405
         * <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
406
         * area but with the items bigger.</p>
407
         */
408
        public static double ZOOMINFACTOR=2;
409

    
410
        /**
411
         * <p>Default <i>zoom out</i> factor.</p>
412
         * <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
413
         * area but with the items smaller.</p>
414
         */
415
        public static double ZOOMOUTFACTOR=0.5;
416

    
417
        /**
418
         * <p>Color used to represent the selections.</p>
419
         */
420
        private static Color selectionColor = Color.YELLOW;
421

    
422

    
423
        /**
424
         * <p>Gets the color used to represent the selections.</p>
425
         *
426
         * @return color used to represent the selections
427
         */
428
        public static Color getSelectionColor() {
429
                return selectionColor;
430
        }
431

    
432
        /**
433
         * <p>Sets the color used to represent the selections.</p>
434
         *
435
         * @param selectionColor color used to represent the selections
436
         */
437
        public static void setSelectionColor(Color selectionColor) {
438
                MapContext.selectionColor = selectionColor;
439
        }
440

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

    
451
                layerEventListener = new LayerEventListener();
452
                layers.addLayerCollectionListener(layerEventListener);
453
                layers.addLayerCollectionListener(eventBuffer);
454

    
455
                setViewPort(vp);
456

    
457
        }
458

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

    
468
                layerEventListener = new LayerEventListener();
469
                layers.addLayerCollectionListener(layerEventListener);
470
                layers.addLayerCollectionListener(eventBuffer);
471

    
472
                setViewPort(vp);
473
        }
474

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

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

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

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

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

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

    
561
        // SUGERENCIA DE PABLO:
562
        //public void removeErrorListener(ErrorListener listener) {
563
        //        errorListeners.remove(listener);
564
        //}
565

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

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

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

    
629
        /**
630
         * <p>Removes the specified layer listener from this map.</p>
631
         *
632
         * @param listener the listener to remove
633
         *
634
         * @see #addLayerListener(LegendListener)
635
         * @see #callLegendChanged()
636
         */
637
        public void removeLayerListener(LegendListener listener) {
638
                legendListeners.remove(listener);
639
        }
640

    
641
        // SUGERENCIA DE PABLO:
642
        // public void removeLegendListener(LegendListener listener) {
643
        //         legendListeners.remove(listener);
644
        // }
645

    
646
        /**
647
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
648
         *
649
         * @return the hierarchy of nodes of layers stored in this map
650
         */
651
        public FLayers getLayers() {
652
                return layers;
653
        }
654

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

    
663
        /**
664
         * @see #redraw()
665
         */
666
        public void invalidate() {
667
                if (getLayers().getLayersCount() > 0)
668
                        getLayers().moveTo(0, 0);
669
        }
670

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

    
692
                Cancellable cancel = new Cancellable() {
693
                        public boolean isCanceled() {
694
                                return false;
695
                        }
696

    
697
                        public void setCanceled(boolean canceled) {
698
                                // No queremos que se pueda cancelar la impresi?n.
699

    
700
                        }
701
                };
702
                layers.print(g, viewPort, cancel, scale, properties);
703
                tracLayer.draw(null, g, viewPort, cancel, scale);
704
        }
705

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

    
717
                return ret;
718
        }
719

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

    
744
                                // TODO Auto-generated catch block
745
                                e.printStackTrace();
746
                                throw new XMLException(e);
747
                        }
748
                }
749
                ret.layers = aux;
750
                return ret;
751

    
752
//                return createFromXML(getXMLEntity());
753

    
754
        }
755

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

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

    
777
        /*
778
         * public void addLayer(LayerPath parent, FLayer layer) throws
779
         * ProjectionMismatchException, ChildrenNotAllowedException {
780
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
781
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
782
         */
783

    
784
        /**
785
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
786
         *
787
         * @param vectorial the layer to add
788
         */
789
        public void addToTrackLayer(FLayer vectorial) {
790
        }
791

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

    
810
                if (viewPort.getImageSize() == null)
811
                        return -1;
812

    
813
                if (viewPort.getAdjustedExtent() == null) {
814
                        return 0;
815
                }
816
                double[] trans2Meter=getDistanceTrans2Meter();
817
                if (proj == null) {
818
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
819
                        return (long) (viewPort.getAdjustedExtent().getWidth() / w * trans2Meter[getViewPort()
820
                                        .getMapUnits()]);
821
                }
822

    
823
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*trans2Meter[getViewPort().getMapUnits()]),
824
                                (viewPort.getAdjustedExtent().getMaxX()*trans2Meter[getViewPort().getMapUnits()]), viewPort.getImageSize()
825
                                                .getWidth(), dpi));
826

    
827
        }
828

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

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

    
866
        /**
867
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
868
         */
869
        public void setVectorial(VectorialAdapter v) {
870
        }
871

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

    
878
        /**
879
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
880
         */
881
        public void processSelected(FeatureVisitor visitor) {
882
        }
883

    
884
        /**
885
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
886
         *      VectorialSubSet)
887
         */
888
        public void select(FeatureVisitor visitor) {
889
        }
890

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

    
897
        /**
898
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
899
         */
900
        public void createIndex() {
901
        }
902

    
903
        /**
904
         * @see org.cresques.geo.Projected#getProjection()
905
         *
906
         * @see ViewPort#getProjection()
907
         * @see #setProjection(IProjection)
908
         * @see #reProject(ICoordTrans)
909
         */
910
        public IProjection getProjection() {
911
                return getViewPort().getProjection();
912
        }
913

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

    
929
        /**
930
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
931
         */
932
        public void reProject(ICoordTrans arg0) {
933
                // TODO implementar reprojecci?n (lo que sea eso)
934
        }
935

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

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

    
965
        /**
966
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
967
         *      int)
968
         */
969
        public void selectByShape(IGeometry g, int relationship) {
970
        }
971

    
972
        /**
973
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#queryByPoint(Point2D,
974
         *      double)
975
         */
976
        public Record[] queryByPoint(Point2D p, double tolerance) {
977
                return null;
978
        }
979

    
980
        /**
981
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#queryByRect(java.awt.geom.Rectangle2D)
982
         */
983
        public Record[] queryByRect(Rectangle2D rect) {
984
                return null;
985
        }
986

    
987
        /**
988
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#queryByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
989
         *      int)
990
         */
991
        public Record[] queryByShape(IGeometry g, int relationship) {
992
                return null;
993
        }
994

    
995
        /**
996
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#getSelectionBounds()
997
         *
998
         * @see SelectedZoomVisitor#getSelectBound()
999
         */
1000
        public Rectangle2D getSelectionBounds() {
1001
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
1002

    
1003
                try {
1004
                        layers.process(visitor);
1005
                } catch (ReadDriverException e1) {
1006
                        throw new RuntimeException(
1007
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
1008
                                        e1);
1009
                } catch (VisitorException e) {
1010
                        throw new RuntimeException(
1011
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
1012
                                        e);
1013
                }
1014

    
1015
                return visitor.getSelectBound();
1016
        }
1017

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

    
1048
                prepareDrawing(image, g, scale);
1049

    
1050
                // M?s c?lidad al texto
1051
                RenderingHints renderHints = new RenderingHints(
1052
                                RenderingHints.KEY_ANTIALIASING,
1053
                                RenderingHints.VALUE_ANTIALIAS_ON);
1054
                renderHints.put(RenderingHints.KEY_RENDERING,
1055
                                RenderingHints.VALUE_RENDER_QUALITY);
1056
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
1057
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1058
                g.setRenderingHints(renderHints);
1059

    
1060
                long t1 = System.currentTimeMillis();
1061
                layers.draw(image, g, viewPort, cancel, scale);
1062

    
1063
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
1064
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1065
                fireLayerDrawingEvent(beforeTracLayerEvent);
1066
                tracLayer.draw(image, g, viewPort, cancel, scale);
1067
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
1068
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1069
                fireLayerDrawingEvent(afterTracLayerEvent);
1070

    
1071
                //layers.setDirty(false);
1072
                long t2 = System.currentTimeMillis();
1073
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
1074
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
1075
                /*
1076
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
1077
                 * GeneralPath(viewPort.getExtent());
1078
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
1079
                 */
1080
                System.gc();
1081
        }
1082

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

    
1095
                // Primera pasada: si alguna capa necesita repintarse por debajo
1096
                // de la que tiene la cache, TODAS necesitan
1097
                // ser repintadas.
1098
                boolean bNeedRepaint = false;
1099
                boolean bMayExistAceleration = false;
1100
                for (int i = 0; i < layers.getLayersCount(); i++)
1101
                {
1102
                        FLayer lyr = layers.getLayer(i);
1103
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1104
                        {
1105
                                bMayExistAceleration = true;
1106
                        }
1107
                        else
1108
                        {
1109
                                if (lyr.isDirty())
1110
                                        bNeedRepaint = true;
1111
                        }
1112
                }
1113
                if (bMayExistAceleration==false)
1114
                        bNeedRepaint = true;
1115
                if (bNeedRepaint)
1116
                        layers.setDirty(true);
1117
                else
1118
                        recursivePrepareDrawing(layers);
1119
        }
1120

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

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

    
1174
                        if (lyr instanceof FLayers)
1175
                        {
1176
                                recursivePrepareDrawing((FLayers)lyr);
1177
                        }
1178
                }
1179

    
1180
        }
1181

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

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

    
1223
                        public void setCanceled(boolean canceled) {
1224
                                // TODO Auto-generated method stub
1225

    
1226
                        }
1227
                }, scale);
1228
        }
1229

    
1230
        /**
1231
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1232
         *
1233
         * @return the view port
1234
         *
1235
         * @see #setViewPort(ViewPort)
1236
         */
1237
        public ViewPort getViewPort() {
1238
                return viewPort;
1239
        }
1240

    
1241
        /**
1242
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1243
         *  of this map.</p>
1244
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1245
         *  adds the new one.</p>
1246
         *
1247
         * @param viewPort the viewPort
1248
         *
1249
         * @see #getViewPort()
1250
         */
1251
        public void setViewPort(ViewPort viewPort) {
1252
                if (this.viewPort != null) {
1253
                        this.viewPort.removeViewPortListener(eventBuffer);
1254
                }
1255

    
1256
                this.viewPort = viewPort;
1257
                if (viewPort != null)
1258
                        viewPort.addViewPortListener(eventBuffer);
1259
        }
1260

    
1261
        /**
1262
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1263
         *
1264
         * @param extent the extent of the new zoom
1265
         */
1266
        public void zoomToExtent(Rectangle2D extent) {
1267
                if (extent!=null)
1268
                        getViewPort().setExtent(extent);
1269
        }
1270

    
1271
        /**
1272
         * <p>Returns the union of all extents of all layers of this map.</p>
1273
         *
1274
         * @return full extent of layers of this map
1275
         * @throws ReadDriverException if the driver fails reading.
1276
         *
1277
         * @see FLayers#getFullExtent()
1278
         */
1279
        public Rectangle2D getFullExtent() throws ReadDriverException {
1280
                return layers.getFullExtent();
1281
        }
1282

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

    
1304
                return xml;
1305
        }
1306

    
1307
        /**
1308
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1309
         *  the data of the {@link ViewPort ViewPort} and
1310
         *  {@link FLayers FLayers}.</p>
1311
         *
1312
         * @param xml an XML entity
1313
         *
1314
         * @return the new <code>MapContext</code> instance
1315
         *
1316
         * @throws XMLException if there is any error creating the map from the XML.
1317
         *
1318
         * @see #getXMLEntity()
1319
         * @see #createFromXML(XMLEntity)
1320
         * @see ViewPort#createFromXML03(XMLEntity)
1321
         * @see FLayers#setXMLEntity03(XMLEntity)
1322
         */
1323
        public static MapContext createFromXML03(XMLEntity xml) throws XMLException {
1324
                ViewPort vp = ViewPort.createFromXML03(xml.getChild(0));
1325
                MapContext fmap = new MapContext(vp);
1326
                fmap.layers.setXMLEntity03(xml.getChild(1));
1327

    
1328
                return fmap;
1329
        }
1330

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

    
1355
        /**
1356
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1357
         *
1358
         * @param listener the new listener
1359
         *
1360
         * @return <code>true</code> if has added the listener successfully
1361
         *
1362
         * @see #removeAtomicEventListener(AtomicEventListener)
1363
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1364
         */
1365
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1366
                return eventBuffer.addAtomicEventListener(listener);
1367
        }
1368

    
1369
        /**
1370
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1371
         *
1372
         * @param listener the listener to remove
1373
         *
1374
     * @return <tt>true</tt> if the list contained the specified element
1375
         *
1376
         * @see #addAtomicEventListener(AtomicEventListener)
1377
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1378
         */
1379
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1380
                return eventBuffer.removeAtomicEventListener(listener);
1381
        }
1382

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

    
1392
        /**
1393
         * @see EventBuffer#endAtomicEvent()
1394
         *
1395
         * @see #beginAtomicEvent()
1396
         */
1397
        public void endAtomicEvent() {
1398
                eventBuffer.endAtomicEvent();
1399
        }
1400

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

    
1426
                        // Registramos al FMap como listener del legend de las capas
1427
                        FLayer lyr = e.getAffectedLayer();
1428
                        selectionListener(lyr);
1429
                }
1430

    
1431
                /**
1432
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1433
                 *
1434
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1435
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1436
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1437
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1438
                 *
1439
                 * @param the layer or layers
1440
                 */
1441
                private void selectionListener(FLayer lyr){
1442
                        lyr.addLayerListener(eventBuffer);
1443

    
1444
                        if (lyr instanceof Classifiable) {
1445
                                Classifiable c = (Classifiable) lyr;
1446
                                c.addLegendListener(eventBuffer);
1447
                        }
1448

    
1449
                        if (lyr instanceof AlphanumericData) {
1450
                                Selectable s=null;
1451
                                try {
1452
                                        s = ((AlphanumericData) lyr).getRecordset();
1453
                                        if (s!=null) {
1454
                                                s.addSelectionListener(eventBuffer);
1455
                                        }
1456
                                } catch (ReadDriverException e1) {
1457
                                        e1.printStackTrace();
1458
                                }
1459

    
1460
                        }
1461
                        if (lyr instanceof FLayers){
1462
                                FLayers lyrs=(FLayers)lyr;
1463
                                for(int i=0;i<lyrs.getLayersCount();i++){
1464
                                        selectionListener(lyrs.getLayer(i));
1465
                                }
1466
                        }
1467

    
1468
                }
1469
                /*
1470
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1471
                 */
1472
                public void layerMoved(LayerPositionEvent e) {
1473
                }
1474

    
1475
                /*
1476
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1477
                 */
1478
                public void layerRemoved(LayerCollectionEvent e) {
1479
                        FLayer lyr = e.getAffectedLayer();
1480

    
1481
                        lyr.removeLayerListener(eventBuffer);
1482

    
1483
                        if (lyr instanceof Classifiable) {
1484
                                Classifiable c = (Classifiable) lyr;
1485
                                c.removeLegendListener(eventBuffer);
1486
                        }
1487

    
1488
                        if (lyr instanceof Selectable) {
1489
                                Selectable s = (Selectable) lyr;
1490
                                s.addSelectionListener(eventBuffer);
1491
                        }
1492
                }
1493

    
1494
                /*
1495
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1496
                 */
1497
                public void layerAdding(LayerCollectionEvent e)
1498
                                throws CancelationException {
1499
                }
1500

    
1501
                /*
1502
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1503
                 */
1504
                public void layerMoving(LayerPositionEvent e)
1505
                                throws CancelationException {
1506
                }
1507

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

    
1515

    
1516
                /*
1517
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1518
                 */
1519
                public void visibilityChanged(LayerCollectionEvent e)
1520
                                throws CancelationException {
1521
                }
1522
        }
1523

    
1524
        /**
1525
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1526
         *  collection of layers argument.</p>
1527
         *
1528
         * @param a collection of layers
1529
         */
1530
        public void addAsCollectionListener(FLayers layers2) {
1531
                layers2.addLayerCollectionListener(layerEventListener);
1532
        }
1533

    
1534
        /**
1535
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1536
         *
1537
         * @return the graphic layer of this map
1538
         *
1539
         * @see #setGraphicsLayer(GraphicLayer)
1540
         */
1541
        public GraphicLayer getGraphicsLayer() {
1542
                return tracLayer;
1543
        }
1544

    
1545
        /**
1546
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1547
         *
1548
         * @param graphicLayer the new graphic layer
1549
         *
1550
         * @see #getGraphicsLayer()
1551
         */
1552
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1553
                tracLayer = graphicLayer;
1554
        }
1555

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

    
1581
                                if (!getLayers().getLayer(i).getName().equals(
1582
                                                map.getLayers().getLayer(i).getName())) {
1583
                                        isEqual = false;
1584
                                }
1585

    
1586
                        }
1587
                } else {
1588
                        isEqual = false;
1589
                }
1590
                return isEqual;
1591
        }
1592

    
1593
        /**
1594
         * <p>Registers the message of an error associated to this map.</p>
1595
         *
1596
         * @param stringProperty the error message
1597
         *
1598
         * @see #getLayersError()
1599
         * @see #clearErrors()
1600
         */
1601
        public void addLayerError(String stringProperty) {
1602
                layersError.add(stringProperty);
1603
        }
1604

    
1605
        /**
1606
         * <p>Gets the list with all error messages registered to this map.</p>
1607
         *
1608
         * @return the list of errors registered to this map
1609
         *
1610
         * @see #addLayerError(String)
1611
         * @see #clearErrors()
1612
         */
1613
        public ArrayList getLayersError() {
1614
                return layersError;
1615
        }
1616

    
1617
        /**
1618
         * <p>Removes all error messages associated to this map.</p>
1619
         *
1620
         * @see #addLayerError(String)
1621
         * @see #getLayersError()
1622
         */
1623
        public void clearErrors() {
1624
                layersError.clear();
1625
        }
1626

    
1627
        /**
1628
         * <p>Removes from this map, all caching images of drawn layers, registered.</p>
1629
         *
1630
         * @see #clearCachingImageDrawnLayers(FLayers)
1631
         */
1632
        public void clearAllCachingImageDrawnLayers() {
1633
                clearCachingImageDrawnLayers(this.layers);
1634
        }
1635

    
1636
        /**
1637
         * <p>Removes from the layer collection argument, all caching images of drawn layers, registered.</p>
1638
         *
1639
         * @param layers a layer collection
1640
         *
1641
         * @see #clearAllCachingImageDrawnLayers()
1642
         */
1643
        private void clearCachingImageDrawnLayers(FLayers layers){
1644
                int i;
1645
                FLayer layer;
1646
                for (i=0;i< layers.getLayersCount();i++){
1647
                        layer = layers.getLayer(i);
1648
                        if (layer instanceof FLayers) {
1649
                                clearCachingImageDrawnLayers((FLayers)layer);
1650
                        } else {
1651
                                layer.setCacheImageDrawnLayers(null);
1652
                        }
1653
                }
1654
        }
1655

    
1656
        /**
1657
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1658
         *
1659
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1660
         * @return the new layer node
1661
         */
1662
        public FLayers getNewGroupLayer(FLayers parent) {
1663
                FLayers group1 = new FLayers();//(this,parent);
1664
                group1.setMapContext(this);
1665
                group1.setParentLayer(parent);
1666
            return group1;
1667
        }
1668

    
1669
}