Statistics
| Revision:

svn-gvsig-desktop / tags / v1_1_Build_1013 / libraries / libFMap / src / com / iver / cit / gvsig / fmap / MapContext.java @ 13521

History | View | Annotate | Download (46.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 com.iver.cit.gvsig.fmap;
42

    
43
import java.awt.Graphics;
44
import java.awt.Graphics2D;
45
import java.awt.RenderingHints;
46
import java.awt.Toolkit;
47
import java.awt.geom.Rectangle2D;
48
import java.awt.image.BufferedImage;
49
import java.util.ArrayList;
50
import java.util.List;
51
import java.util.prefs.Preferences;
52

    
53
import javax.print.attribute.PrintRequestAttributeSet;
54

    
55
import org.cresques.cts.ICoordTrans;
56
import org.cresques.cts.IProjection;
57
import org.cresques.geo.Projected;
58

    
59
import com.iver.cit.gvsig.fmap.layers.CancelationException;
60
import com.iver.cit.gvsig.fmap.layers.FLayer;
61
import com.iver.cit.gvsig.fmap.layers.FLayers;
62
import com.iver.cit.gvsig.fmap.layers.GraphicLayer;
63
import com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent;
64
import com.iver.cit.gvsig.fmap.layers.LayerCollectionListener;
65
import com.iver.cit.gvsig.fmap.layers.LayerDrawEvent;
66
import com.iver.cit.gvsig.fmap.layers.LayerDrawingListener;
67
import com.iver.cit.gvsig.fmap.layers.LayerPositionEvent;
68
import com.iver.cit.gvsig.fmap.layers.LegendListener;
69
import com.iver.cit.gvsig.fmap.layers.XMLException;
70
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
71
import com.iver.cit.gvsig.fmap.layers.layerOperations.Classifiable;
72
import com.iver.cit.gvsig.fmap.layers.layerOperations.Selectable;
73
import com.iver.cit.gvsig.fmap.operations.strategies.SelectedZoomVisitor;
74
import com.iver.cit.gvsig.fmap.operations.strategies.VisitException;
75
import com.iver.utiles.XMLEntity;
76
import com.iver.utiles.swing.threads.Cancellable;
77

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

    
134
        /**
135
         * <p>Defines the value which a unit of a distance measurement must be divided to obtain its equivalent <b>in centimeters</b>.</p>
136
         * 
137
         * <p><b><i>Conversion values of distance measurements:</i></b>
138
         * <ul>
139
         *  <li><code>MapContext.CHANGEM[0]</code>: kilometer
140
         *  <li><code>MapContext.CHANGEM[1]</code>: meter
141
         *  <li><code>MapContext.CHANGEM[2]</code>: centimeter
142
         *  <li><code>MapContext.CHANGEM[3]</code>: millimeter
143
         *  <li><code>MapContext.CHANGEM[4]</code>: statute mile
144
         *  <li><code>MapContext.CHANGEM[5]</code>: yard
145
         *  <li><code>MapContext.CHANGEM[6]</code>: foot
146
         *  <li><code>MapContext.CHANGEM[7]</code>: inch
147
         *  <li><code>MapContext.CHANGEM[8]</code>: grade
148
         * </ul>
149
         * 
150
         * <p><h3>Examples:</h3>
151
         * <pre>1 statute mile / MapContext.CHANGEM[4] = X centimeters</pre>
152
         * <pre>1 kilometer / MapContext.CHANGEM[0] = X centimeters</pre>
153
         * <pre>1 grade / MapContext.CHANGEM[8] = X centimeters</pre>
154
         * </p>
155
         * 
156
         * <p><h3>Grade conversion value: <code>MapContext.CHANGEM[8]</code></h3> 
157
         * The value of <code>MapContext.CHANGEM[8]</code> represents the centimeters of a straight line between two
158
         *  points on the Earth surface that are 1 grade far each other of the center of the Earth. This value has been calculated using 
159
         *  a radius approximated of R<sub>Earth</sub>=6.37846082678100774672e6 meters, according these equations:
160
         * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
161
         * <pre>MapContext.CHANGEM[8] = 1 / D</pre>
162
         * <h4>Explanation:</h4>
163
         * We get an isosceles triangle with the center of the Earth and the 2 points on the surface. This triangle can be divided into
164
         * 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
165
         * is the hypotenuse. Then we apply trigonometry and get the distance <i>D</i> between both points on the Earth surface.</p>
166
         * <p>Now we only must invert that value to obtain <code>MapContext.CHANGEM[8]</code>.</p>
167
         */
168
        public static final double[] CHANGE = { 100000, 100, 1, 0.1, 160934.4,
169
                        91.44, 30.48, 2.54, 1/8.983152841195214E-4 };
170

    
171
        /**
172
         * 
173
         */
174
        public static final int EQUALS = 0;
175

    
176
        /**
177
         * 
178
         */
179
        public static final int DISJOINT = 1;
180

    
181
        /**
182
         * 
183
         */
184
        public static final int INTERSECTS = 2;
185

    
186
        /**
187
         * 
188
         */
189
        public static final int TOUCHES = 3;
190

    
191
        /**
192
         * 
193
         */
194
        public static final int CROSSES = 4;
195

    
196
        /**
197
         * 
198
         */
199
        public static final int WITHIN = 5;
200

    
201
        /**
202
         * 
203
         */
204
        public static final int CONTAINS = 6;
205

    
206
        /**
207
         * 
208
         */
209
        public static final int OVERLAPS = 7;
210

    
211
        /**
212
         * A hierarchy of {@link FLayers FLayers} nodes.
213
         * 
214
         * @see #getLayers()
215
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
216
         */
217
        protected FLayers layers;
218

    
219
        /**
220
         * A layer with graphical items: geometries and symbols.
221
         * 
222
         * @see #getGraphicsLayer()
223
         * @see #setGraphicsLayer(GraphicLayer)
224
         * @see #drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
225
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
226
         */
227
        private GraphicLayer tracLayer = new GraphicLayer();
228

    
229
        /**
230
         * Information for draw the view.
231
         * 
232
         * @see #getViewPort()
233
         * @see #setViewPort(ViewPort)
234
         */
235
        private ViewPort viewPort;
236

    
237
        // private ArrayList invalidationListeners = new ArrayList();
238

    
239
        /**
240
         * Array list with all {@link LegendListener LegendListener} registered to this map.
241
         * 
242
         * @see #addLayerListener(LegendListener)
243
         * @see #removeLayerListener(LegendListener)
244
         * @see #callLegendChanged()
245
         */
246
        private ArrayList legendListeners = new ArrayList();
247

    
248
        /**
249
         * Array list with all {@link LayerDrawingListener LayerDrawingListener} registered to this map.
250
         * 
251
         * @see #addLayerDrawingListener(LayerDrawingListener)
252
         * @see #removeLayerDrawListener(LayerDrawingListener)
253
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
254
         */
255
        private ArrayList layerDrawingListeners = new ArrayList();
256

    
257
        /**
258
         * <p>Buffer that is used to store and eject events produced on this map:
259
         * <ul>
260
         *  <li>Layer collection events.
261
         *  <li>View port events.
262
         *  <li>Atomic events.
263
         *  <li>Layer events.
264
         *  <li>Legend events on a {@link Classificable Classificable} layer.
265
         *  <li>Selection events on an {@link AlphanumericData AlphanumericData} data layer.
266
         * </ul>
267
         * </p>
268
         * 
269
         * @see #addAtomicEventListener(AtomicEventListener)
270
         * @see #removeAtomicEventListener(AtomicEventListener)
271
         * @see #beginAtomicEvent()
272
         * @see #endAtomicEvent()
273
         */
274
        private EventBuffer eventBuffer = new EventBuffer();
275

    
276
        /**
277
         * Event listener for the collection of layers of this map.
278
         */
279
        private LayerEventListener layerEventListener = null;
280

    
281
        /**
282
         * List with information of all errors produced on all layers.
283
         * 
284
         * @see #addLayerError(String)
285
         * @see #getLayersError()
286
         * @see #clearErrors()
287
         */
288
        private ArrayList layersError = new ArrayList();
289

    
290
        /**
291
         * Array list with all {@link ErrorListener ErrorListener} registered to this map.
292
         * 
293
         * @see #addErrorListener(ErrorListener)
294
         * @see #removeErrorListener(LegendListener)
295
         * @see #callNewErrorEvent(ErrorEvent)
296
         * @see #reportDriverExceptions(String, List)
297
         */
298
        private ArrayList errorListeners = new ArrayList();
299

    
300
        // public static ResourceBundle myResourceBundle =
301
        // ResourceBundle.getBundle("FMap");
302

    
303
        /**
304
         * <p>Default zoom in factor.</p>
305
         * <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
306
         * area but with the items bigger.</p>
307
         */
308
        public static double ZOOMINFACTOR=2;
309

    
310
        /**
311
         * <p>Default zoom out factor.</p>
312
         * <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
313
         * area but with the items smaller.</p>
314
         */
315
        public static double ZOOMOUTFACTOR=0.5;
316

    
317
        /**
318
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
319
         *  without layers.</p>
320
         *
321
         * @param vp information for drawing the layers of this map as a view
322
         */
323
        public MapContext(ViewPort vp) {
324
                this.layers = new FLayers(this, null);
325

    
326
                layerEventListener = new LayerEventListener();
327
                layers.addLayerCollectionListener(layerEventListener);
328
                layers.addLayerCollectionListener(eventBuffer);
329

    
330
                setViewPort(vp);
331
        }
332

    
333
        /**
334
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
335
         * 
336
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
337
         * @param vp information for drawing the layers of this map as a view
338
         */
339
        public MapContext(FLayers fLayers, ViewPort vp) {
340
                this.layers = fLayers;
341

    
342
                layerEventListener = new LayerEventListener();
343
                layers.addLayerCollectionListener(layerEventListener);
344
                layers.addLayerCollectionListener(eventBuffer);
345

    
346
                setViewPort(vp);
347
        }
348

    
349
        /**
350
         * <p>Reports to all driver error listeners registered of a bundle of driver exceptions caused in the same map atomic transaction.</p> 
351
         * 
352
         * @param introductoryText introductory text specified by developer. If <code>null</code>, use ""
353
         * @param driverExceptions list with a bundle of driver exceptions caught during an atomic event
354
         * 
355
         * @see #addErrorListener(ErrorListener)
356
         * @see #removeErrorListener(LegendListener)
357
         * @see #callNewErrorEvent(ErrorEvent)
358
         */
359
        public synchronized void reportDriverExceptions(String introductoryText,
360
                                                                                                        List driverExceptions){
361
                for (int i = 0; i < errorListeners.size(); i++) {
362
                        ((ErrorListener) errorListeners.get(i)).
363
                                reportDriverExceptions(introductoryText, driverExceptions);
364
                }
365
        }
366

    
367
        /**
368
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
369
         *
370
         * @param listener the legend listener
371
         * 
372
         * @see #removeLayerListener(LegendListener)
373
         * @see #callLegendChanged()
374
         */
375
        public void addLayerListener(LegendListener listener) {
376
                if (!legendListeners.contains(listener))
377
                        legendListeners.add(listener);
378
        }
379
        // SUGERENCIA DE PABLO
380
        //        public void addLegendListener(LegendListener listener) {
381
        //                if (!legendListeners.contains(listener))
382
        //                        legendListeners.add(listener);
383
        //        }
384

    
385
        /**
386
         * <p>Adds the specified layer drawing listener to receive drawing events from layers of this map.</p>
387
         * 
388
         * @param listener the listener to add
389
         * 
390
         * @see #removeLayerDrawListener(LayerDrawingListener)
391
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
392
         */
393
        public void addLayerDrawingListener(LayerDrawingListener listener) {
394
                layerDrawingListeners.add(listener);
395
        }
396

    
397
        /**
398
         * <p>Removes the specified layer drawing listener from this map.</p>
399
         * 
400
         * @param listener the listener to remove
401
         * 
402
         * @see #addLayerDrawingListener(LayerDrawingListener)
403
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
404
         */
405
        public void removeLayerDrawListener(LayerDrawingListener listener) {
406
                layerDrawingListeners.remove(listener);
407
        }
408

    
409
        /**
410
         * <p>Adds the specified error listener to receive error events from this map.</p>
411
         * 
412
         * @param listener the listener to add
413
         * 
414
         * @see #removeErrorListener(LegendListener)
415
         * @see #callNewErrorEvent(ErrorEvent)
416
         * @see #reportDriverExceptions(String, List)
417
         */
418
        public void addErrorListener(ErrorListener listener) {
419
                errorListeners.add(listener);
420
        }
421

    
422
        /**
423
         * <p>Removes the specified error listener from this map.</p>
424
         * 
425
         * @param listener the listener to remove
426
         * 
427
         * @see #addErrorListener(ErrorListener)
428
         * @see #callNewErrorEvent(ErrorEvent)
429
         * @see #reportDriverExceptions(String, List)
430
         */
431
        public void removeErrorListener(LegendListener listener) {
432
                legendListeners.remove(listener);
433
        }
434
        // SUGERENCIA DE PABLO:
435
        //public void removeErrorListener(ErrorListener listener) {
436
        //        errorListeners.remove(listener);
437
        //}
438

    
439
        /**
440
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
441
         * <p>This method must be call only if it's wanted to reflect a legend change.</p>
442
         * 
443
         * @see #addLayerListener(LegendListener)
444
         * @see #removeLayerListener(LegendListener)
445
         */
446
        public synchronized void callLegendChanged() {
447
                for (int i = 0; i < legendListeners.size(); i++) {
448
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
449
                }
450
                // getLayers().moveTo(0,0);
451
        }
452

    
453
        /**
454
         * <p>Fires a layer drawing event to all {@link LayerDrawingListener LayerDrawingListener} listeners registered,
455
         *  distinguishing the kind of event.</p>
456
         * 
457
         * @param e the event
458
         *
459
         * @see #addLayerDrawingListener(LayerDrawingListener)
460
         * @see #removeLayerDrawListener(LayerDrawingListener)
461
         */
462
        public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
463
                for (int i = 0; i < layerDrawingListeners.size(); i++)
464
                {
465
                        LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
466
                        switch (e.getEventType())
467
                        {
468
                                case LayerDrawEvent.LAYER_BEFORE_DRAW:
469
                                        listener.beforeLayerDraw(e);
470
                                        break;
471
                                case LayerDrawEvent.LAYER_AFTER_DRAW:
472
                                        listener.afterLayerDraw(e);
473
                                        break;
474
                                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
475
                                        listener.beforeGraphicLayerDraw(e);
476
                                        break;
477
                                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
478
                                        listener.afterLayerGraphicDraw(e);
479
                                        break;
480
                        }
481
                }
482
                // getLayers().moveTo(0,0);
483
        }
484

    
485
        /**
486
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
487
         * 
488
         * @param e the event with information of the error
489
         * 
490
         * @see #addErrorListener(ErrorListener)
491
         * @see #removeErrorListener(LegendListener)
492
         * @see #reportDriverExceptions(String, List)
493
         */
494
        public synchronized void callNewErrorEvent(ErrorEvent e) {
495
                for (int i = 0; i < errorListeners.size(); i++) {
496
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
497
                }
498
                // getLayers().moveTo(0,0);
499
        }
500

    
501
        /**
502
         * <p>Removes the specified layer listener from this map.</p>
503
         * 
504
         * @param listener the listener to remove
505
         * 
506
         * @see #addLayerListener(LegendListener)
507
         * @see #callLegendChanged()
508
         */
509
        public void removeLayerListener(LegendListener listener) {
510
                legendListeners.remove(listener);
511
        }
512
        // SUGERENCIA DE PABLO:
513
        // public void removeLegendListener(LegendListener listener) {
514
        //         legendListeners.remove(listener);
515
        // }
516

    
517
        /**
518
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
519
         *
520
         * @return the hierarchy of nodes of layers stored in this map
521
         */
522
        public FLayers getLayers() {
523
                return layers;
524
        }
525

    
526
        /**
527
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
528
         *
529
         * @param b image with an accessible buffer of image data
530
         */
531
        public void drawLabels(BufferedImage b) {
532
        }
533

    
534
        /**
535
         * @see #redraw()
536
         */
537
        public void invalidate() {
538
                // SUGERENCIA DE PABLO:
539
                // redraw();
540
                
541
                getLayers().moveTo(0, 0);
542
        }
543

    
544
        /**
545
         * <p>Prints the layers of this map using the {@link Graphics2D Graphics2D} argument, that usually is
546
         * the {@link Graphics Graphics} of the printer.</p>
547
         *
548
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
549
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
550
         * @param properties a set with the settings to be applied to a whole print job and to all the documents in the print job
551
         *
552
         * @throws DriverException if fails using some driver.
553
         * 
554
         * @see FLayers#print(Graphics2D, ViewPort, Cancellable, double, PrintRequestAttributeSet)
555
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
556
         */
557
        public void print(Graphics2D g, double scale, PrintRequestAttributeSet properties) throws DriverException {
558
                RenderingHints renderHints = new RenderingHints(
559
                                RenderingHints.KEY_ANTIALIASING,
560
                                RenderingHints.VALUE_ANTIALIAS_ON);
561
                renderHints.put(RenderingHints.KEY_RENDERING,
562
                                RenderingHints.VALUE_RENDER_QUALITY);
563
                g.setRenderingHints(renderHints);
564

    
565
                Cancellable cancel = new Cancellable() {
566
                        /*
567
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
568
                         */
569
                        public boolean isCanceled() {
570
                                return false;
571
                        }
572

    
573
                        /*
574
                         * @see com.iver.utiles.swing.threads.Cancellable#setCanceled(boolean)
575
                         */
576
                        public void setCanceled(boolean canceled) {
577
                                // No queremos que se pueda cancelar la impresi?n.
578

    
579
                        }
580
                };
581
                layers.print(g, viewPort, cancel, scale, properties);
582
                tracLayer.draw(null, g, viewPort, cancel, scale);
583
        }
584

    
585
        /**
586
         * <p>Returns a new map with the information of the <code>vp</code> argument, and the layers of this map.</p>
587
         *
588
         * @param vp information for drawing the layers
589
         *
590
         * @return a new map
591
         */
592
        public MapContext createNewFMap(ViewPort vp) {
593
                MapContext ret = new MapContext(vp);
594
                ret.layers = this.layers;
595

    
596
                return ret;
597
        }
598

    
599
        /**
600
         * <p>Creates a new independent map, that has a clone of the layers and the view port of this one.</p>
601
         * <p>The new map will have the same data source drivers to avoid waste memory, and work faster.</p>
602
         *
603
         * @return the new map
604
         *
605
         * @throws XMLException if fails cloning the view port or a layer
606
         * 
607
         * @see FLayer#cloneLayer()
608
         * @see ViewPort#cloneViewPort()
609
         */
610
        public MapContext cloneFMap() throws XMLException {
611
                ViewPort vp = getViewPort().cloneViewPort();
612
                FLayers antLayers = getLayers();
613
                MapContext ret = new MapContext(vp);
614
                FLayers aux = new FLayers(ret, null);
615
                for (int i=0; i < antLayers.getLayersCount(); i++)
616
                {
617
                        FLayer lyr = antLayers.getLayer(i);
618
                        try {
619
                                aux.addLayer(lyr.cloneLayer());
620
                        } catch (Exception e) {
621
                                throw new XMLException(e);
622
                        }
623
                }
624
                ret.layers = aux;
625
                return ret;
626

    
627
//                return createFromXML(getXMLEntity());
628
        }
629

    
630
        /**
631
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
632
         * 
633
         * @return the new map
634
         */
635
        public MapContext cloneToDraw() {
636
                ViewPort vp = getViewPort().cloneViewPort();
637
                MapContext mapContext=new MapContext(getLayers(),vp);
638
                return mapContext;
639
        }
640

    
641
        /**
642
         * A?ade la capa que se pasa como par?metro al nodo que se pasa como
643
         * parametro y lanza ProjectionMismatchException si no est?n todas las capas
644
         * de este FMap en la misma proyecci?n. Lanza un ChildNotAllowedException si
645
         * la capa no es un FLayers y no permite hijos
646
         *
647
         * @param vectorial
648
         *            DOCUMENT ME!
649
         */
650

    
651
        /*
652
         * public void addLayer(LayerPath parent, FLayer layer) throws
653
         * ProjectionMismatchException, ChildrenNotAllowedException {
654
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
655
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
656
         */
657

    
658
        /**
659
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
660
         *
661
         * @param vectorial the layer to add
662
         */
663
        public void addToTrackLayer(FLayer vectorial) {
664
        }
665

    
666
        /**
667
         * <p>Returns the scale of the view in the screen.</p>
668
         *
669
         * @return one of this values:
670
         * <ul>
671
         * <li>the scale of the adjusted extent scale of the view in the screen
672
         * <li><code>-1</code> if there is no image
673
         * <li><code>0</code> if there is no extent defined for the image
674
         * </ul>
675
         * 
676
         * @see #setScaleView(long)
677
         * @see ViewPort#getAdjustedExtent()
678
         * @see IProjection#getScale(double, double, double, double)
679
         */
680
        public long getScaleView() {
681
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
682
                Toolkit kit = Toolkit.getDefaultToolkit();
683
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
684
                IProjection proj = viewPort.getProjection();
685

    
686
                if (viewPort.getImageSize() == null)
687
                        return -1;
688

    
689
                if (viewPort.getAdjustedExtent() == null) {
690
                        return 0;
691
                }
692

    
693
                if (proj == null) {
694
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
695
                        return (long) (viewPort.getAdjustedExtent().getWidth() / w * CHANGEM[getViewPort()
696
                                        .getMapUnits()]/CHANGEM[getViewPort().getDistanceUnits()]);
697
                }
698
                if (proj.isProjected())
699
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*CHANGEM[getViewPort().getMapUnits()])/CHANGEM[getViewPort().getDistanceUnits()],
700
                                (viewPort.getAdjustedExtent().getMaxX()*CHANGEM[getViewPort().getMapUnits()])/CHANGEM[getViewPort().getDistanceUnits()], viewPort.getImageSize()
701
                                                .getWidth(), dpi));
702
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*CHANGEM[1])/CHANGEM[getViewPort().getDistanceUnits()],
703
                                (viewPort.getAdjustedExtent().getMaxX()*CHANGEM[1])/CHANGEM[getViewPort().getDistanceUnits()], viewPort.getImageSize()
704
                                                .getWidth(), dpi));
705
        }
706
        /**
707
         * <p>Sets the new extent of the view, calculated using the scale argument.</p>
708
         * <p>Doesn't updates the scale if there isn't information about the dimension of the image or the
709
         *  adjusted extent.</p>
710
         *
711
         * @param scale the new scale for the view
712
         * 
713
         * @see ViewPort#setProjection(IProjection)
714
         * @see #getScaleView()
715
         */
716
        public void setScaleView(long scale) {
717
                clearAllCachingImageDrawnLayers();
718
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
719
                Toolkit kit = Toolkit.getDefaultToolkit();
720
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
721
                if (viewPort.getImageSize() == null)
722
                        return;
723
                IProjection proj = viewPort.getProjection();
724
                if (viewPort.getAdjustedExtent() == null) {
725
                        return;
726
                }
727
                Rectangle2D rec=proj.getExtent(
728
                                viewPort.getAdjustedExtent(),
729
                                scale,viewPort.getImageWidth(),
730
                                viewPort.getImageHeight(),
731
                                CHANGE[getViewPort().getMapUnits()],
732
//                                CHANGEM[getViewPort().getDistanceUnits()],
733
                                dpi);
734
//                double dif=CHANGEM[getViewPort().getDistanceUnits()];
735
//                double w=rec.getWidth()/dif;
736
//                double h=rec.getHeight()/dif;
737
//                rec.setRect(rec.getCenterX()-w/2,rec.getCenterY()-h/2,w,h);
738
                getViewPort().setExtent(rec);
739
        }
740

    
741
        /**
742
         * @see org.cresques.geo.Projected#getProjection()
743
         * 
744
         * @see ViewPort#getProjection()
745
         * @see #setProjection(IProjection)
746
         * @see #reProject(ICoordTrans)
747
         */
748
        public IProjection getProjection() {
749
                return getViewPort().getProjection();
750
        }
751

    
752
        /**
753
         * <p>Sets the new projection.</p>
754
         *
755
         * @param proj the new projection
756
         * 
757
         * @see #getProjection()
758
         * @see ViewPort#setProjection(IProjection)
759
         * @see #reProject(ICoordTrans)
760
         */
761
        public void setProjection(IProjection proj) {
762
                if (getViewPort() != null) {
763
                        getViewPort().setProjection(proj);
764
                }
765
        }
766

    
767
        /*
768
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
769
         */
770
        public void reProject(ICoordTrans arg0) {
771
                // TODO implementar reprojecci?n (lo que sea eso)
772
        }
773

    
774
        /**
775
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#getSelectionBounds()
776
         * 
777
         * @see SelectedZoomVisitor#getSelectBound()
778
         */
779
        public Rectangle2D getSelectionBounds() {
780
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
781

    
782
                try {
783
                        layers.process(visitor);
784
                } catch (DriverException e1) {
785
                        throw new RuntimeException(
786
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
787
                                        e1);
788
                } catch (VisitException e) {
789
                        throw new RuntimeException(
790
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
791
                                        e);
792
                }
793

    
794
                return visitor.getSelectBound();
795
        }
796

    
797
        /**
798
         * <p>Draws this map if its {@link ViewPort ViewPort} has an extent defined:<br>
799
         * <ol>
800
         * <li>Selects only the layers that have to be drawn: {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
801
         * <li>Sets quality: antialiasing por text and images, and quality rendering. 
802
         * <li>Draws the layers.
803
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
804
         * <li>Draws the graphic layer.
805
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
806
         * <li>Invokes the garbage collector and memory clean.
807
         * </ol></p>
808
         * 
809
         * @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.
810
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
811
         * @param cancel an object thread that implements the {@link Cancellable Cancellable} interface, and will allow to cancel the draw
812
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
813
         * @throws DriverException if fails using some driver.
814
         */
815
        public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
816
                        double scale) throws DriverException {
817
                if (viewPort.getExtent() == null) {
818
                        // System.err.println("viewPort.getExtent() = null");
819
                        return;
820
                }
821
                System.out.println("Viewport despues: " + viewPort.toString());
822
                /*
823
                 * if ((viewPort.getImageWidth() <=0) || (viewPort.getImageHeight() <=
824
                 * 0)) { return; }
825
                 */
826

    
827
                prepareDrawing(image, g, scale);
828

    
829
                // M?s c?lidad al texto
830
                RenderingHints renderHints = new RenderingHints(
831
                                RenderingHints.KEY_ANTIALIASING,
832
                                RenderingHints.VALUE_ANTIALIAS_ON);
833
                renderHints.put(RenderingHints.KEY_RENDERING,
834
                                RenderingHints.VALUE_RENDER_QUALITY);
835
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
836
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
837
                g.setRenderingHints(renderHints);
838

    
839
                long t1 = System.currentTimeMillis();
840
                layers.draw(image, g, viewPort, cancel, scale);
841

    
842
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
843
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
844
                fireLayerDrawingEvent(beforeTracLayerEvent);
845
                tracLayer.draw(image, g, viewPort, cancel, scale);
846
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
847
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
848
                fireLayerDrawingEvent(afterTracLayerEvent);
849

    
850
                //layers.setDirty(false);
851

    
852
                long t2 = System.currentTimeMillis();
853
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
854
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
855
                /*
856
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
857
                 * GeneralPath(viewPort.getExtent());
858
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
859
                 */
860
                System.gc();
861
        }
862

    
863
        /**
864
         * <p>Checks out layers that need to be repainted.</p>
865
         * <p>If one layer node uses a cache with previous image drawn, but hasn't any, or if it's dirty, then,
866
         *  that layer must be repainted. </p>
867
         * <p>If one layer node needn't to be repainted, checks out it recursively.</p>
868
         * <p><i>The cache of "previous image drawn" allows accelerate the repaint process.</i></p>
869
         * 
870
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
871
         * @see #recursivePrepareDrawing(FLayers, int)
872
         */
873
        private void prepareDrawing(BufferedImage image, Graphics2D g, double scale) {
874

    
875
                // Primera pasada: si alguna capa necesita repintarse por debajo
876
                // de la que tiene la cache, TODAS necesitan
877
                // ser repintadas.
878
                boolean bNeedRepaint = false;
879
                boolean bMayExistAceleration = false;
880
                for (int i = 0; i < layers.getLayersCount(); i++)
881
                {
882
                        FLayer lyr = layers.getLayer(i);
883
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
884
                        {
885
                                bMayExistAceleration = true;
886
                        }
887
                        else
888
                        {
889
                                if (lyr.isDirty())
890
                                        bNeedRepaint = true;
891
                        }
892
                }
893
                if (bMayExistAceleration==false)
894
                        bNeedRepaint = true;
895
                if (bNeedRepaint)
896
                        layers.setDirty(true);
897
                else
898
                        recursivePrepareDrawing(layers, 0);
899
        }
900

    
901
        /**
902
         * <p>Invoked by {@linkplain MapContext#recursivePrepareDrawing(FLayers, int)}. Sets all previous layer nodes
903
         *  in the argument as not dirty, what means that don't need to be repainted.</p>
904
         * <p>Each layer node can decide validate or not it's sub-layers according its implementation.</p>
905
         * <p>This is useful when it's editing, for accelerate the draw, because the layers of a {@link FLayers FLayers} node
906
         * are painted according its index in the collection, and each one upper the previous.</p>
907
         * 
908
         * @param layers a node with layers 
909
         * @param index index of the current layer in the internal list of layers
910
         * 
911
         * @see #recursivePrepareDrawing(FLayers, int)
912
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
913
         */
914
        private void validatePreviousLayers(FLayers layers, int index)
915
        {
916
                // TODO: Aqu? quiz?s habr?a que explorar los padres de las capas
917
                // para marcar y/o asignar la imagen cacheada.
918
                for (int i = 0; i < index; i++)
919
                {
920
                        FLayer lyr = layers.getLayer(i);
921
                        lyr.setDirty(false);
922
                }
923
                // Las de arriba las marcamos como sucias
924
//                for (int i = index; i < layers.getLayersCount(); i++)
925
//                {
926
//                        FLayer lyr = layers.getLayer(i);
927
//                        lyr.setDirty(true);
928
//                }
929
        }
930

    
931
        /**
932
         * <p>Checks out recursively, layers that have a cache with an image of previous layers drawn, and if are dirty
933
         * notify the previous layers that are valid.</p>
934
         * 
935
         * @param parent node that contains the layers
936
         * @param indexInParent the least layer index in the <code>parent</code> argument. This allows reduce the time processing
937
         *  if is known that the first layers aren't a collection and will (or won't) be validated
938
         * 
939
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
940
         */
941
        private void recursivePrepareDrawing(FLayers parent, int indexInParent)
942
        {
943
//       SUGERENCIA (de Pablo): Intentar evitar que pueda revalidar varias veces las mismas capas, dado
944
//                  que la imagen de anteriores capas dibujadas contendr? todas las capas previas, y con ello
945
//                  obtener un ligero aumento de velocidad.
946
//
947
//                boolean validated = false;
948
//                FLayer lyr;
949
//                
950
//                for (int i = parent.getLayersCount()-1; i >= indexInParent; i--) {
951
//                        lyr = layers.getLayer(i);
952
//
953
//                        if ((!validated) && (lyr.isCachingDrawnLayers()) && (lyr.getCacheImageDrawnLayers() != null))
954
//                        {
955
//                                // les decimos a las anteriores que est?n validadas (not dirty)
956
//                                // para que no se dibujen.
957
//                                if (lyr.isDirty()) {
958
//                                        validatePreviousLayers(parent, i);
959
//                                        validated = true;
960
//                                }
961
//                        }
962
//
963
//                        if (lyr instanceof FLayers)
964
//                        {
965
//                                recursivePrepareDrawing((FLayers)lyr, 0);
966
//                        }
967
//                }
968

    
969
                for (int i = indexInParent; i < parent.getLayersCount(); i++)
970
                {
971
                        FLayer lyr = layers.getLayer(i);
972
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
973
                        {
974
                                // les decimos a las anteriores que est?n validadas (not dirty)
975
                                // para que no se dibujen.
976
                                if (lyr.isDirty())
977
                                        validatePreviousLayers(parent, i);
978
                        }
979

    
980
                        if (lyr instanceof FLayers)
981
                        {
982
                                recursivePrepareDrawing((FLayers)lyr, 0);
983
                        }
984
                }
985
        }
986

    
987
        /**
988
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
989
         * 
990
         * @param image image used to accelerate the screen draw
991
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
992
         * @param cancel an object thread that implements the {@link Cancellable Cancellable} interface, and will allow to cancel the draw
993
         * @param scale value that represents the scale
994
         * @throws DriverException if fails using some driver.
995
         * 
996
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
997
         */
998
        public void drawGraphics(BufferedImage image, Graphics2D g,
999
                        Cancellable cancel, double scale) throws DriverException {
1000
                if (viewPort == null)
1001
                        return;
1002
                tracLayer.draw(image, g, viewPort, cancel, scale);
1003
        }
1004

    
1005
        /**
1006
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1007
         *  the task as cancellable.</p>
1008
         * 
1009
         * @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.
1010
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1011
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1012
         * 
1013
         * @throws DriverException if fails using some driver.
1014
         * 
1015
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1016
         */
1017
        public void draw(BufferedImage image, Graphics2D g, double scale)
1018
                        throws DriverException {
1019
                layers.setDirty(true);
1020
                draw(image, g, new Cancellable() {
1021
                        /*
1022
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1023
                         */
1024
                        public boolean isCanceled() {
1025
                                return false;
1026
                        }
1027

    
1028
                        /*
1029
                         * @see com.iver.utiles.swing.threads.Cancellable#setCanceled(boolean)
1030
                         */
1031
                        public void setCanceled(boolean canceled) {
1032
                                // TODO Auto-generated method stub
1033

    
1034
                        }
1035
                }, scale);
1036
        }
1037

    
1038
        /**
1039
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1040
         *
1041
         * @return the view port
1042
         * 
1043
         * @see #setViewPort(ViewPort)
1044
         */
1045
        public ViewPort getViewPort() {
1046
                return viewPort;
1047
        }
1048

    
1049
        /**
1050
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1051
         *  of this map.</p>
1052
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1053
         *  adds the new one.</p>
1054
         *
1055
         * @param viewPort the viewPort
1056
         * 
1057
         * @see #getViewPort()
1058
         */
1059
        public void setViewPort(ViewPort viewPort) {
1060
                if (this.viewPort != null) {
1061
                        this.viewPort.removeViewPortListener(eventBuffer);
1062
                }
1063

    
1064
                this.viewPort = viewPort;
1065
                if (viewPort != null)
1066
                        viewPort.addViewPortListener(eventBuffer);
1067
        }
1068

    
1069
        /**
1070
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1071
         *
1072
         * @param extent the extent of the new zoom
1073
         */
1074
        public void zoomToExtent(Rectangle2D extent) {
1075
            if (extent!=null)
1076
                    getViewPort().setExtent(extent);
1077
        }
1078

    
1079
        /**
1080
         * <p>Returns the union of all extents of all layers of this map.</p>
1081
         *
1082
         * @return full extent of layers of this map
1083
         * @throws DriverException if fails using a driver.
1084
         * 
1085
         * @see FLayers#getFullExtent()
1086
         */
1087
        public Rectangle2D getFullExtent() throws DriverException {
1088
                return layers.getFullExtent();
1089
        }
1090

    
1091
        /**
1092
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1093
         * <ul>
1094
         * <li>XML of the internal {@link ViewPort ViewPort}.
1095
         * <li>XML of the internal {@link FLayers FLayers}.
1096
         * </ul>
1097
         * 
1098
         * @return XMLEntity the XML entity
1099
         * @throws XMLException if there is any error creating the XML from the map.
1100
         * 
1101
         * @see #createFromXML(XMLEntity)
1102
         * @see #createFromXML03(XMLEntity)
1103
         * @see ViewPort#getXMLEntity()
1104
         * @see FLayers#getXMLEntity()
1105
         */
1106
        public XMLEntity getXMLEntity() throws XMLException {
1107
                XMLEntity xml = new XMLEntity();
1108
                xml.putProperty("className", this.getClass().getName());
1109
                xml.addChild(viewPort.getXMLEntity());
1110
                xml.addChild(layers.getXMLEntity());
1111

    
1112
                return xml;
1113
        }
1114

    
1115
        /**
1116
         * <p>Creates a new <code>MapContext</code> from an XML entity, with
1117
         *  with the data of the {@link ViewPort ViewPort} and
1118
         *  {@link FLayers FLayers}.</p>
1119
         *
1120
         * @param xml an XML entity
1121
         *
1122
         * @return the new <code>MapContext</code>
1123
         *
1124
         * @throws XMLException if there is any error creating the map from the XML.
1125
         * 
1126
         * @see #getXMLEntity()
1127
         * @see #createFromXML(XMLEntity)
1128
         * @see ViewPort#createFromXML03(XMLEntity)
1129
         * @see FLayers#setXMLEntity03(XMLEntity)
1130
         */
1131
        public static MapContext createFromXML03(XMLEntity xml) throws XMLException {
1132
                ViewPort vp = ViewPort.createFromXML03(xml.getChild(0));
1133
                MapContext fmap = new MapContext(vp);
1134
                fmap.layers.setXMLEntity03(xml.getChild(1));
1135

    
1136
                return fmap;
1137
        }
1138

    
1139
        /**
1140
         * <p>Creates a new <code>MapContext</code> from an XML entity, with
1141
         *  with the data of the {@link ViewPort ViewPort} and
1142
         *  {@link FLayers FLayers}.</p>
1143
         *
1144
         * @param xml an XML entity
1145
         *
1146
         * @return the new <code>MapContext</code>
1147
         *
1148
         * @throws XMLException if there is any error creating the map from the XML.
1149
         * 
1150
         * @see #getXMLEntity()
1151
         * @see #createFromXML03(XMLEntity)
1152
         * @see ViewPort#createFromXML(XMLEntity)
1153
         * @see FLayers#setXMLEntity(XMLEntity)
1154
         */
1155
        public static MapContext createFromXML(XMLEntity xml) throws XMLException {
1156
                ViewPort vp = ViewPort.createFromXML(xml.getChild(0));
1157
                MapContext fmap = new MapContext(vp);
1158
                fmap.layers.setXMLEntity(xml.getChild(1));
1159

    
1160
                return fmap;
1161
        }
1162

    
1163
        /**
1164
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1165
         *
1166
         * @param listener the new listener
1167
         *
1168
         * @return <code>true</code> if has added the listener successfully
1169
         * 
1170
         * @see #removeAtomicEventListener(AtomicEventListener) 
1171
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1172
         */
1173
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1174
                return eventBuffer.addAtomicEventListener(listener);
1175
        }
1176

    
1177
        /**
1178
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1179
         *
1180
         * @param listener the listener to remove
1181
         * 
1182
     * @return <tt>true</tt> if the list contained the specified element
1183
         * 
1184
         * @see #addAtomicEventListener(AtomicEventListener)
1185
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1186
         */
1187
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1188
                return eventBuffer.removeAtomicEventListener(listener);
1189
        }
1190

    
1191
        /**
1192
         * @see EventBuffer#beginAtomicEvent()
1193
         * 
1194
         * @see #endAtomicEvent()
1195
         */
1196
        public void beginAtomicEvent() {
1197
                eventBuffer.beginAtomicEvent();
1198
        }
1199

    
1200
        /**
1201
         * @see EventBuffer#endAtomicEvent()
1202
         * 
1203
         * @see #beginAtomicEvent()
1204
         */
1205
        public void endAtomicEvent() {
1206
                eventBuffer.endAtomicEvent();
1207
        }
1208

    
1209
        /**
1210
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1211
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1212
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1213
         *
1214
         * @author Fernando Gonz?lez Cort?s
1215
         */
1216
        public class LayerEventListener implements LayerCollectionListener {
1217
                /*
1218
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1219
                 */
1220
                public void layerAdded(LayerCollectionEvent e) {
1221
                        // Si es la primera capa, fijamos su extent al ViewPort
1222
                        // if (getLayers().getLayersCount() == 1) {
1223
                        if (getViewPort().getExtent() == null) {
1224
                                FLayer lyr = e.getAffectedLayer();
1225

    
1226
                                try {
1227
                                        getViewPort().setExtent(lyr.getFullExtent());
1228
                                } catch (DriverException e1) {
1229
                                }
1230
                        }
1231

    
1232
                        // Registramos al FMap como listener del legend de las capas
1233
                        FLayer lyr = e.getAffectedLayer();
1234
                        selectionListener(lyr);
1235
                }
1236

    
1237
                /**
1238
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1239
                 *
1240
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1241
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1242
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one, 
1243
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p> 
1244
                 * 
1245
                 * @param the layer or layers
1246
                 */
1247
                private void selectionListener(FLayer lyr){
1248
                        lyr.addLayerListener(eventBuffer);
1249

    
1250
                        if (lyr instanceof Classifiable) {
1251
                                Classifiable c = (Classifiable) lyr;
1252
                                c.addLegendListener(eventBuffer);
1253
                        }
1254

    
1255
                        if (lyr instanceof AlphanumericData) {
1256
                                Selectable s=null;
1257
                                try {
1258
                                        s = ((AlphanumericData) lyr).getRecordset();
1259
                                        if (s!=null) {
1260
                                                s.addSelectionListener(eventBuffer);
1261
                                        }
1262
                                } catch (DriverException e1) {
1263
                                        // TODO Auto-generated catch block
1264
                                        e1.printStackTrace();
1265
                                }
1266

    
1267
                        }
1268
                        if (lyr instanceof FLayers){
1269
                                FLayers lyrs=(FLayers)lyr;
1270
                                for(int i=0;i<lyrs.getLayersCount();i++){
1271
                                        selectionListener(lyrs.getLayer(i));
1272
                                }
1273
                        }
1274
                }
1275

    
1276
                /*
1277
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1278
                 */
1279
                public void layerMoved(LayerPositionEvent e) {
1280
                }
1281

    
1282
                /*
1283
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1284
                 */
1285
                public void layerRemoved(LayerCollectionEvent e) {
1286
                        FLayer lyr = e.getAffectedLayer();
1287

    
1288
                        lyr.removeLayerListener(eventBuffer);
1289

    
1290
                        if (lyr instanceof Classifiable) {
1291
                                Classifiable c = (Classifiable) lyr;
1292
                                c.removeLegendListener(eventBuffer);
1293
                        }
1294

    
1295
                        if (lyr instanceof Selectable) {
1296
                                Selectable s = (Selectable) lyr;
1297
                                s.addSelectionListener(eventBuffer);
1298
                        }
1299
                }
1300

    
1301
                /*
1302
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1303
                 */
1304
                public void layerAdding(LayerCollectionEvent e)
1305
                                throws CancelationException {
1306
                }
1307

    
1308
                /*
1309
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1310
                 */
1311
                public void layerMoving(LayerPositionEvent e)
1312
                                throws CancelationException {
1313
                }
1314

    
1315
                /*
1316
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1317
                 */
1318
                public void layerRemoving(LayerCollectionEvent e)
1319
                                throws CancelationException {
1320
                }
1321

    
1322
                /*
1323
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1324
                 */
1325
                public void visibilityChanged(LayerCollectionEvent e)
1326
                                throws CancelationException {
1327
                }
1328
        }
1329

    
1330
        /**
1331
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the 
1332
         *  collection of layers argument.</p> 
1333
         * 
1334
         * @param a collection of layers
1335
         */
1336
        public void addAsCollectionListener(FLayers layers2) {
1337
                layers2.addLayerCollectionListener(layerEventListener);
1338
        }
1339

    
1340
        /**
1341
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1342
         * 
1343
         * @return the graphic layer of this map
1344
         * 
1345
         * @see #setGraphicsLayer(GraphicLayer)
1346
         */
1347
        public GraphicLayer getGraphicsLayer() {
1348
                return tracLayer;
1349
        }
1350

    
1351
        /**
1352
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1353
         * 
1354
         * @param graphicLayer the new graphic layer
1355
         * 
1356
         * @see #getGraphicsLayer()
1357
         */
1358
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1359
                tracLayer = graphicLayer;
1360
        }
1361

    
1362
        /**
1363
         * <p>Indicates whether some other object is "equal to" this map.</p>
1364
         * <p>Returns <code>true</code> if success on this options:
1365
         * <ol>
1366
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1367
         * <li>Both maps have the same layers.
1368
         * <li>Both maps have the same number of layers and with the same name.
1369
         * </ol>
1370
         * </p>
1371
         * 
1372
         * @param obj the reference object with which to compare.
1373
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument; <code>false</code> otherwise.
1374
         *  
1375
         * @see Object#equals(Object)
1376
         */
1377
        public boolean equals(Object arg0) {
1378
                MapContext map = (MapContext) arg0;
1379
                if (super.equals(arg0))
1380
                        return true;
1381
                if (getLayers() == map.getLayers())
1382
                        return true;
1383
                boolean isEqual = true;
1384
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1385
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1386

    
1387
                                if (!getLayers().getLayer(i).getName().equals(
1388
                                                map.getLayers().getLayer(i).getName())) {
1389
                                        isEqual = false;
1390
                                }
1391

    
1392
                        }
1393
                } else {
1394
                        isEqual = false;
1395
                }
1396
                return isEqual;
1397
        }
1398

    
1399
        /**
1400
         * <p>Registers the message of an error associated to this map.</p>
1401
         * 
1402
         * @param stringProperty the error message
1403
         * 
1404
         * @see #getLayersError()
1405
         * @see #clearErrors()
1406
         */
1407
        public void addLayerError(String stringProperty) {
1408
                layersError.add(stringProperty);
1409
        }
1410

    
1411
        /**
1412
         * <p>Gets the list with all errors messages registered to this map.</p>
1413
         * 
1414
         * @return the list of errors registered to this map
1415
         * 
1416
         * @see #addLayerError(String)
1417
         * @see #clearErrors()
1418
         */
1419
        public ArrayList getLayersError() {
1420
                return layersError;
1421
        }
1422

    
1423
        /**
1424
         * <p>Removes all error messages associated to this map.</p>
1425
         * 
1426
         * @see #addLayerError(String)
1427
         * @see #getLayersError()
1428
         */
1429
        public void clearErrors() {
1430
                layersError.clear();
1431
        }
1432

    
1433
        /**
1434
         * <p>Removes from this map, all caching images of drawn layers, registered.</p>
1435
         * 
1436
         * @see #clearCachingImageDrawnLayers(FLayers)
1437
         */
1438
        public void clearAllCachingImageDrawnLayers() {
1439
                clearCachingImageDrawnLayers(this.layers);
1440
        }
1441
        
1442
        /**
1443
         * <p>Removes from the layer collection argument, all caching images of drawn layers, registered.</p>
1444
         * 
1445
         * @param layers a layer collection
1446
         * 
1447
         * @see #clearAllCachingImageDrawnLayers()
1448
         */
1449
        private void clearCachingImageDrawnLayers(FLayers layers){
1450
                int i;
1451
                FLayer layer;
1452
                for (i=0;i< layers.getLayersCount();i++){
1453
                        layer = layers.getLayer(i);
1454
                        if (layer instanceof FLayers) {
1455
                                clearCachingImageDrawnLayers((FLayers)layer);
1456
                        } else {
1457
                                layer.setCacheImageDrawnLayers(null);
1458
                        }
1459
                }
1460
        }
1461

    
1462
        /**
1463
         * <p>Applies for a refresh everything what depends on this map: TOC, <code>MapControl</code>,
1464
         *  <code>FFrameView</code>, ... To do this, it produces an event of layer order change.</p> 
1465
         */
1466
        public void redraw() {
1467
                // truco
1468
                if (getLayers().getLayersCount() > 0)
1469
                        getLayers().moveTo(0,0);
1470
        }
1471
}