Statistics
| Revision:

svn-gvsig-desktop / branches / v10 / libraries / libFMap / src / com / iver / cit / gvsig / fmap / MapContext.java @ 13720

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
331
                setViewPort(vp);
332
        }
333

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

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

    
347
                setViewPort(vp);
348
        }
349

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
597
                return ret;
598
        }
599

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
795
                return visitor.getSelectBound();
796
        }
797

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

    
828
                prepareDrawing(image, g, scale);
829

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

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

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

    
851
                //layers.setDirty(false);
852

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

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

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

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

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

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

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

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

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

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

    
1035
                        }
1036
                }, scale);
1037
        }
1038

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

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

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

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

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

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

    
1113
                return xml;
1114
        }
1115

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

    
1137
                return fmap;
1138
        }
1139

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

    
1161
                return fmap;
1162
        }
1163

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1289
                        lyr.removeLayerListener(eventBuffer);
1290

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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