Statistics
| Revision:

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

History | View | Annotate | Download (55.2 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.fmap.data.exceptions.ReadException;
60
import org.gvsig.fmap.data.feature.FeatureStoreNotification;
61
import org.gvsig.fmap.geom.primitive.DefaultEnvelope;
62
import org.gvsig.fmap.geom.primitive.Envelope;
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.operations.Classifiable;
77
import org.gvsig.fmap.mapcontext.layers.operations.SingleLayer;
78
import org.gvsig.fmap.mapcontext.rendering.legend.events.listeners.LegendListener;
79
import org.gvsig.fmap.mapcontext.rendering.strategies.SelectedZoomVisitor;
80
import org.gvsig.tools.exception.BaseException;
81
import org.gvsig.tools.observer.DefaultObservable;
82
import org.gvsig.tools.observer.Observable;
83
import org.gvsig.tools.observer.Observer;
84
import org.gvsig.tools.visitor.Visitor;
85

    
86
import com.iver.utiles.IPersistence;
87
import com.iver.utiles.XMLEntity;
88
import com.iver.utiles.XMLException;
89
import com.iver.utiles.swing.threads.Cancellable;
90

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

    
149

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

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

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

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

    
179

    
180
    }
181

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

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

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

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

    
326
        public static final int EQUALS = 0;
327

    
328
        public static final int DISJOINT = 1;
329

    
330
        public static final int INTERSECTS = 2;
331

    
332
        public static final int TOUCHES = 3;
333

    
334
        public static final int CROSSES = 4;
335

    
336
        public static final int WITHIN = 5;
337

    
338
        public static final int CONTAINS = 6;
339

    
340
        public static final int OVERLAPS = 7;
341

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

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

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

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

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

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

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

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

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

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

    
431

    
432

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

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

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

    
450
        /**
451
         * <p>Color used to represent the selections.</p>
452
         */
453
        private static Color selectionColor = Color.YELLOW;
454
        private DefaultObservable defaultObservable=new DefaultObservable();
455

    
456
        private ArrayList snappers = new ArrayList();
457
        private ArrayList layersToSnap = new ArrayList();
458

    
459

    
460
        /**
461
         * <p>Gets the color used to represent the selections.</p>
462
         *
463
         * @return color used to represent the selections
464
         */
465
        public static Color getSelectionColor() {
466
                return selectionColor;
467
        }
468

    
469
        /**
470
         * <p>Sets the color used to represent the selections.</p>
471
         *
472
         * @param selectionColor color used to represent the selections
473
         */
474
        public static void setSelectionColor(Color selectionColor) {
475
                MapContext.selectionColor = selectionColor;
476
        }
477

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

    
488
                layerEventListener = new LayerEventListener();
489
                layers.addLayerCollectionListener(layerEventListener);
490
                layers.addLayerCollectionListener(eventBuffer);
491

    
492
                setViewPort(vp);
493

    
494
        }
495

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

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

    
509
                setViewPort(vp);
510
        }
511

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

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

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

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

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

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

    
599
        // SUGERENCIA DE PABLO:
600
        //public void removeErrorListener(ErrorListener listener) {
601
        //        errorListeners.remove(listener);
602
        //}
603

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

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

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

    
667
        /**
668
         * <p>Removes the specified layer listener from this map.</p>
669
         *
670
         * @param listener the listener to remove
671
         *
672
         * @see #addLayerListener(LegendListener)
673
         * @see #callLegendChanged()
674
         */
675
        public void removeLayerListener(LegendListener listener) {
676
                legendListeners.remove(listener);
677
        }
678

    
679
        // SUGERENCIA DE PABLO:
680
        // public void removeLegendListener(LegendListener listener) {
681
        //         legendListeners.remove(listener);
682
        // }
683

    
684
        /**
685
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
686
         *
687
         * @return the hierarchy of nodes of layers stored in this map
688
         */
689
        public FLayers getLayers() {
690
                return layers;
691
        }
692

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

    
701
        /**
702
         * @see #redraw()
703
         */
704
        public void invalidate() {
705
                if (getLayers().getLayersCount() > 0) {
706
                        getLayers().moveTo(0, 0);
707
                }
708
        }
709

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

    
731
                Cancellable cancel = new Cancellable() {
732
                        public boolean isCanceled() {
733
                                return false;
734
                        }
735

    
736
                        public void setCanceled(boolean canceled) {
737
                                // No queremos que se pueda cancelar la impresi?n.
738

    
739
                        }
740
                };
741
                layers.print(g, viewPort, cancel, scale, properties);
742
                tracLayer.draw(null, g, viewPort, cancel, scale);
743
        }
744

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

    
756
                return ret;
757
        }
758

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

    
783
                                // TODO Auto-generated catch block
784
                                e.printStackTrace();
785
                        }
786
                }
787
                ret.layers = aux;
788
                return ret;
789

    
790
//                return createFromXML(getXMLEntity());
791

    
792
        }
793

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

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

    
815
        /*
816
         * public void addLayer(LayerPath parent, FLayer layer) throws
817
         * ProjectionMismatchException, ChildrenNotAllowedException {
818
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
819
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
820
         */
821

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

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

    
848
                if (viewPort.getImageSize() == null) {
849
                        return -1;
850
                }
851

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

    
862
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinimum(0)*trans2Meter[getViewPort().getMapUnits()]),
863
                                (viewPort.getAdjustedExtent().getMaximum(0)*trans2Meter[getViewPort().getMapUnits()]), viewPort.getImageSize()
864
                                                .getWidth(), dpi));
865

    
866
        }
867

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

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

    
908
        /**
909
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
910
         */
911
//        public void setVectorial(VectorialAdapter v) {
912
//        }
913

    
914
        /**
915
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
916
         */
917
        public void process(Visitor visitor) {
918
        }
919

    
920
        /**
921
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
922
         */
923
        public void processSelected(Visitor visitor) {
924
        }
925

    
926
        /**
927
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
928
         *      VectorialSubSet)
929
         */
930
        public void select(Visitor visitor) {
931
        }
932

    
933
        /**
934
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#selectFromSelection()
935
         */
936
        public void selectFromSelection() {
937
        }
938

    
939
        /**
940
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#createIndex()
941
         */
942
        public void createIndex() {
943
        }
944

    
945
        /**
946
         * @see org.cresques.geo.Projected#getProjection()
947
         *
948
         * @see ViewPort#getProjection()
949
         * @see #setProjection(IProjection)
950
         * @see #reProject(ICoordTrans)
951
         */
952
        public IProjection getProjection() {
953
                return getViewPort().getProjection();
954
        }
955

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

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

    
978
        /**
979
         * @see org.gvsig.fmap.mapcontext.rendering.strategies.Strategy#getSelectionBounds()
980
         *
981
         * @see SelectedZoomVisitor#getSelectBound()
982
         */
983
        public Envelope getSelectionBounds() throws BaseException {
984
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
985

    
986
                layers.accept(visitor);
987

    
988
                return visitor.getSelectBound();
989
        }
990

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

    
1021
                prepareDrawing(image, g, scale);
1022

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

    
1033
                long t1 = System.currentTimeMillis();
1034
                layers.draw(image, g, viewPort, cancel, scale);
1035

    
1036
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
1037
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1038
                fireLayerDrawingEvent(beforeTracLayerEvent);
1039
                tracLayer.draw(image, g, viewPort, cancel, scale);
1040
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
1041
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1042
                fireLayerDrawingEvent(afterTracLayerEvent);
1043

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

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

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

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

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

    
1151
                        if (lyr instanceof FLayers)
1152
                        {
1153
                                recursivePrepareDrawing((FLayers)lyr);
1154
                        }
1155
                }
1156

    
1157
        }
1158

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

    
1178
        /**
1179
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1180
         *  the task as cancellable.</p>
1181
         *
1182
         * @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.
1183
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1184
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1185
         *
1186
         * @throws ReadDriverException if the driver fails reading.
1187
         *
1188
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1189
         */
1190
        public void draw(BufferedImage image, Graphics2D g, double scale)
1191
                        throws ReadException {
1192
                layers.setDirty(true);
1193
                draw(image, g, new Cancellable() {
1194
                        /**
1195
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1196
                         */
1197
                        public boolean isCanceled() {
1198
                                return false;
1199
                        }
1200

    
1201
                        public void setCanceled(boolean canceled) {
1202
                                // TODO Auto-generated method stub
1203

    
1204
                        }
1205
                }, scale);
1206
        }
1207

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

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

    
1234
                this.viewPort = viewPort;
1235
                if (viewPort != null) {
1236
                        viewPort.addViewPortListener(eventBuffer);
1237
                }
1238
        }
1239

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

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

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

    
1284
                return xml;
1285
        }
1286

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

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

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

    
1338
        /**
1339
         * @see EventBuffer#beginAtomicEvent()
1340
         *
1341
         * @see #endAtomicEvent()
1342
         */
1343
        public void beginAtomicEvent() {
1344
                eventBuffer.beginAtomicEvent();
1345
        }
1346

    
1347
        /**
1348
         * @see EventBuffer#endAtomicEvent()
1349
         *
1350
         * @see #beginAtomicEvent()
1351
         */
1352
        public void endAtomicEvent() {
1353
                eventBuffer.endAtomicEvent();
1354
        }
1355

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

    
1381
                        // Registramos al FMap como listener del legend de las capas
1382
                        FLayer lyr = e.getAffectedLayer();
1383
                        selectionListener(lyr);
1384
                        if (lyr instanceof SingleLayer){
1385
                                ((SingleLayer)lyr).getDataStore().addObserver(MapContext.this);
1386
                        }
1387
                }
1388

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

    
1402
                        if (lyr instanceof Classifiable) {
1403
                                Classifiable c = (Classifiable) lyr;
1404
                                c.addLegendListener(eventBuffer);
1405
                        }
1406

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

    
1426
                }
1427
                /*
1428
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1429
                 */
1430
                public void layerMoved(LayerPositionEvent e) {
1431
                }
1432

    
1433
                /*
1434
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1435
                 */
1436
                public void layerRemoved(LayerCollectionEvent e) {
1437
                        FLayer lyr = e.getAffectedLayer();
1438

    
1439
                        lyr.removeLayerListener(eventBuffer);
1440

    
1441
                        if (lyr instanceof Classifiable) {
1442
                                Classifiable c = (Classifiable) lyr;
1443
                                c.removeLegendListener(eventBuffer);
1444
                        }
1445

    
1446
                        if (lyr instanceof SingleLayer && ((SingleLayer) lyr).getDataStore()!=null) {
1447
                                ((SingleLayer) lyr).getDataStore().deleteObserver(
1448
                                                MapContext.this);
1449
                        }
1450

    
1451
//                        if (lyr instanceof FLyrVect ) {
1452
//                                Selectable s = (Selectable) lyr;
1453
//                                s.addSelectionListener(eventBuffer);
1454
//                        }
1455
                }
1456

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

    
1464
                /*
1465
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1466
                 */
1467
                public void layerMoving(LayerPositionEvent e)
1468
                                throws CancelationException {
1469
                }
1470

    
1471
                /*
1472
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1473
                 */
1474
                public void layerRemoving(LayerCollectionEvent e)
1475
                                throws CancelationException {
1476
                }
1477

    
1478

    
1479
                /*
1480
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1481
                 */
1482
                public void visibilityChanged(LayerCollectionEvent e)
1483
                                throws CancelationException {
1484
                }
1485
        }
1486

    
1487
        /**
1488
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1489
         *  collection of layers argument.</p>
1490
         *
1491
         * @param a collection of layers
1492
         */
1493
        public void addAsCollectionListener(FLayers layers2) {
1494
                layers2.addLayerCollectionListener(layerEventListener);
1495
        }
1496

    
1497
        /**
1498
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1499
         *
1500
         * @return the graphic layer of this map
1501
         *
1502
         * @see #setGraphicsLayer(GraphicLayer)
1503
         */
1504
        public GraphicLayer getGraphicsLayer() {
1505
                return tracLayer;
1506
        }
1507

    
1508
        /**
1509
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1510
         *
1511
         * @param graphicLayer the new graphic layer
1512
         *
1513
         * @see #getGraphicsLayer()
1514
         */
1515
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1516
                tracLayer = graphicLayer;
1517
        }
1518

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

    
1549
                                if (!getLayers().getLayer(i).getName().equals(
1550
                                                map.getLayers().getLayer(i).getName())) {
1551
                                        isEqual = false;
1552
                                }
1553

    
1554
                        }
1555
                } else {
1556
                        isEqual = false;
1557
                }
1558
                return isEqual;
1559
        }
1560

    
1561
        /**
1562
         * <p>Registers the message of an error associated to this map.</p>
1563
         *
1564
         * @param stringProperty the error message
1565
         *
1566
         * @see #getLayersError()
1567
         * @see #clearErrors()
1568
         */
1569
        public void addLayerError(String stringProperty) {
1570
                layersError.add(stringProperty);
1571
        }
1572

    
1573
        /**
1574
         * <p>Gets the list with all error messages registered to this map.</p>
1575
         *
1576
         * @return the list of errors registered to this map
1577
         *
1578
         * @see #addLayerError(String)
1579
         * @see #clearErrors()
1580
         */
1581
        public ArrayList getLayersError() {
1582
                return layersError;
1583
        }
1584

    
1585
        /**
1586
         * <p>Removes all error messages associated to this map.</p>
1587
         *
1588
         * @see #addLayerError(String)
1589
         * @see #getLayersError()
1590
         */
1591
        public void clearErrors() {
1592
                layersError.clear();
1593
        }
1594

    
1595
        /**
1596
         * <p>Removes from this map, all caching images of drawn layers, registered.</p>
1597
         *
1598
         * @see #clearCachingImageDrawnLayers(FLayers)
1599
         */
1600
        public void clearAllCachingImageDrawnLayers() {
1601
                clearCachingImageDrawnLayers(this.layers);
1602
        }
1603

    
1604
        /**
1605
         * <p>Removes from the layer collection argument, all caching images of drawn layers, registered.</p>
1606
         *
1607
         * @param layers a layer collection
1608
         *
1609
         * @see #clearAllCachingImageDrawnLayers()
1610
         */
1611
        private void clearCachingImageDrawnLayers(FLayers layers){
1612
                int i;
1613
                FLayer layer;
1614
                for (i=0;i< layers.getLayersCount();i++){
1615
                        layer = layers.getLayer(i);
1616
                        if (layer instanceof FLayers) {
1617
                                clearCachingImageDrawnLayers((FLayers)layer);
1618
                        } else {
1619
                                layer.setCacheImageDrawnLayers(null);
1620
                        }
1621
                }
1622
        }
1623

    
1624
        /**
1625
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1626
         *
1627
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1628
         * @return the new layer node
1629
         */
1630
        public FLayers getNewGroupLayer(FLayers parent) {
1631
                FLayers group1 = new FLayers();//(this,parent);
1632
                group1.setMapContext(this);
1633
                group1.setParentLayer(parent);
1634
            return group1;
1635
        }
1636

    
1637
        public void update(Observable observable, Object notification) {
1638
                // TODO REVISAR ESTO!!!
1639
                String ntype=null;
1640
                if (notification instanceof FeatureStoreNotification) {
1641
                        FeatureStoreNotification fsNotification = (FeatureStoreNotification) notification;
1642
                        ntype =fsNotification.getType();
1643
                        if (
1644
                                        ntype.equals(FeatureStoreNotification.LOAD_FINISHED)||
1645
                                        ntype.equals(FeatureStoreNotification.SELECTION_CHANGE)
1646
                        ) {
1647
                                getLayers().moveTo(0, 0);
1648
                        }
1649

    
1650
                }
1651

    
1652
        }
1653

    
1654
        public String getClassName() {
1655
                return null;
1656
        }
1657

    
1658
        public void setXMLEntity(XMLEntity arg0) {
1659
                // TODO Auto-generated method stub
1660

    
1661
        }
1662
        public ArrayList getSnappers() {
1663
                return snappers;
1664
        }
1665
        public void setSnappers(ArrayList snappers){
1666
                this.snappers=snappers;
1667
        }
1668

    
1669
        public ArrayList getLayersToSnap() {
1670
                return layersToSnap;
1671
        }
1672

    
1673
        public void setLayersToSnap(ArrayList layersToSnap) {
1674
                this.layersToSnap = layersToSnap;
1675

    
1676
        }
1677

    
1678
}