Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / MapContext.java @ 20098

History | View | Annotate | Download (50.4 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.Color;
44
import java.awt.Graphics;
45
import java.awt.Graphics2D;
46
import java.awt.RenderingHints;
47
import java.awt.Toolkit;
48
import java.awt.geom.Point2D;
49
import java.awt.geom.Rectangle2D;
50
import java.awt.image.BufferedImage;
51
import java.util.ArrayList;
52
import java.util.List;
53
import java.util.prefs.Preferences;
54

    
55
import javax.print.attribute.PrintRequestAttributeSet;
56

    
57
import org.cresques.cts.ICoordTrans;
58
import org.cresques.cts.IProjection;
59
import org.cresques.geo.Projected;
60

    
61
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
62
import com.hardcode.gdbms.engine.data.driver.DriverException;
63
import com.iver.cit.gvsig.exceptions.visitors.VisitorException;
64
import com.iver.cit.gvsig.fmap.core.IGeometry;
65
import com.iver.cit.gvsig.fmap.layers.CancelationException;
66
import com.iver.cit.gvsig.fmap.layers.FLayer;
67
import com.iver.cit.gvsig.fmap.layers.FLayers;
68
import com.iver.cit.gvsig.fmap.layers.GraphicLayer;
69
import com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent;
70
import com.iver.cit.gvsig.fmap.layers.LayerCollectionListener;
71
import com.iver.cit.gvsig.fmap.layers.LayerDrawEvent;
72
import com.iver.cit.gvsig.fmap.layers.LayerDrawingListener;
73
import com.iver.cit.gvsig.fmap.layers.LayerPositionEvent;
74
import com.iver.cit.gvsig.fmap.layers.VectorialAdapter;
75
import com.iver.cit.gvsig.fmap.layers.XMLException;
76
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
77
import com.iver.cit.gvsig.fmap.layers.layerOperations.Classifiable;
78
import com.iver.cit.gvsig.fmap.layers.layerOperations.Selectable;
79
import com.iver.cit.gvsig.fmap.operations.selection.Record;
80
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
81
import com.iver.cit.gvsig.fmap.operations.strategies.SelectedZoomVisitor;
82
import com.iver.cit.gvsig.fmap.rendering.LegendListener;
83
import com.iver.utiles.XMLEntity;
84
import com.iver.utiles.swing.threads.Cancellable;
85

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

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

    
180
        /* Do not alter the order and the values of this array, if you need append values.*/
181
        /**
182
         * <p>Gets the name of all distance measurements supported by <code>MapContext</code>.</p>
183
         */
184
        public static final String[] NAMES= {
185
                Messages.getString("Kilometros"),
186
                Messages.getString("Metros"),
187
                Messages.getString("Centimetros"),
188
                Messages.getString("Milimetros"),
189
                Messages.getString("Millas"),
190
                Messages.getString("Yardas"),
191
                Messages.getString("Pies"),
192
                Messages.getString("Pulgadas"),
193
                Messages.getString("Grados"),
194
        };
195

    
196
        public static final int EQUALS = 0;
197

    
198
        public static final int DISJOINT = 1;
199

    
200
        public static final int INTERSECTS = 2;
201

    
202
        public static final int TOUCHES = 3;
203

    
204
        public static final int CROSSES = 4;
205

    
206
        public static final int WITHIN = 5;
207

    
208
        public static final int CONTAINS = 6;
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

    
302

    
303
        // public static ResourceBundle myResourceBundle =
304
        // ResourceBundle.getBundle("FMap");
305

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

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

    
320
        /**
321
         * <p>Color used to represent the selections.</p>
322
         */
323
        private static Color selectionColor = Color.YELLOW;
324

    
325

    
326
        /**
327
         * <p>Gets the color used to represent the selections.</p>
328
         *
329
         * @return color used to represent the selections
330
         */
331
        public static Color getSelectionColor() {
332
                return selectionColor;
333
        }
334

    
335
        /**
336
         * <p>Sets the color used to represent the selections.</p>
337
         *
338
         * @param selectionColor color used to represent the selections
339
         */
340
        public static void setSelectionColor(Color selectionColor) {
341
                MapContext.selectionColor = selectionColor;
342
        }
343

    
344
        /**
345
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
346
         *  without layers.</p>
347
         *
348
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
349
         */
350
        public MapContext(ViewPort vp) {
351
                this.layers = new FLayers();//(this,null);
352
                this.layers.setMapContext(this);
353

    
354
                layerEventListener = new LayerEventListener();
355
                layers.addLayerCollectionListener(layerEventListener);
356
                layers.addLayerCollectionListener(eventBuffer);
357

    
358
                setViewPort(vp);
359

    
360
        }
361

    
362
        /**
363
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
364
         * 
365
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
366
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
367
         */
368
        public MapContext(FLayers fLayers, ViewPort vp) {
369
                this.layers = fLayers;
370

    
371
                layerEventListener = new LayerEventListener();
372
                layers.addLayerCollectionListener(layerEventListener);
373
                layers.addLayerCollectionListener(eventBuffer);
374

    
375
                setViewPort(vp);
376
        }
377

    
378
        /**
379
         * <p>Reports to all driver error listeners registered of a bundle of driver exceptions caused in the same map atomic transaction.</p> 
380
         * 
381
         * @param introductoryText introductory text specified by developer. If <code>null</code>, use ""
382
         * @param driverExceptions list with a bundle of driver exceptions caught during an atomic event
383
         * 
384
         * @see #addErrorListener(ErrorListener)
385
         * @see #removeErrorListener(LegendListener)
386
         * @see #callNewErrorEvent(ErrorEvent)
387
         */
388
        public synchronized void reportDriverExceptions(String introductoryText,
389
                                                                                                        List driverExceptions){
390
                for (int i = 0; i < errorListeners.size(); i++) {
391
                        ((ErrorListener) errorListeners.get(i)).
392
                                reportDriverExceptions(introductoryText, driverExceptions);
393
                }
394
        }
395

    
396
        /**
397
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
398
         *
399
         * @param listener the legend listener
400
         * 
401
         * @see #removeLayerListener(LegendListener)
402
         * @see #callLegendChanged()
403
         */
404
        public void addLayerListener(LegendListener listener) {
405
                if (!legendListeners.contains(listener))
406
                        legendListeners.add(listener);
407
        }
408
        // SUGERENCIA DE PABLO
409
        //        public void addLegendListener(LegendListener listener) {
410
        //                if (!legendListeners.contains(listener))
411
        //                        legendListeners.add(listener);
412
        //        }
413

    
414
        /**
415
         * <p>Adds the specified layer drawing listener to catch and handle drawing events from layers of this map.</p>
416
         * 
417
         * @param listener the listener to add
418
         * 
419
         * @see #removeLayerDrawListener(LayerDrawingListener)
420
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
421
         */
422
        public void addLayerDrawingListener(LayerDrawingListener listener) {
423
                layerDrawingListeners.add(listener);
424
        }
425

    
426
        /**
427
         * <p>Removes the specified layer drawing listener from this map.</p>
428
         * 
429
         * @param listener the listener to remove
430
         * 
431
         * @see #addLayerDrawingListener(LayerDrawingListener)
432
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
433
         */
434
        public void removeLayerDrawListener(LayerDrawingListener listener) {
435
                layerDrawingListeners.remove(listener);
436
        }
437

    
438
        /**
439
         * <p>Adds the specified error listener to receive error events from this map.</p>
440
         * 
441
         * @param listener the listener to add
442
         * 
443
         * @see #removeErrorListener(LegendListener)
444
         * @see #callNewErrorEvent(ErrorEvent)
445
         * @see #reportDriverExceptions(String, List)
446
         */
447
        public void addErrorListener(ErrorListener listener) {
448
                errorListeners.add(listener);
449
        }
450

    
451
        /**
452
         * <p>Removes the specified error listener from this map.</p>
453
         * 
454
         * @param listener the listener to remove
455
         * 
456
         * @see #addErrorListener(ErrorListener)
457
         * @see #callNewErrorEvent(ErrorEvent)
458
         * @see #reportDriverExceptions(String, List)
459
         */
460
        public void removeErrorListener(LegendListener listener) {
461
                legendListeners.remove(listener);
462
        }
463

    
464
        // SUGERENCIA DE PABLO:
465
        //public void removeErrorListener(ErrorListener listener) {
466
        //        errorListeners.remove(listener);
467
        //}
468

    
469
        /**
470
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
471
         * <p>This method must be called only if it's wanted to reflect a legend change.</p>
472
         * 
473
         * @see #addLayerListener(LegendListener)
474
         * @see #removeLayerListener(LegendListener)
475
         */
476
        public synchronized void callLegendChanged() {
477
                for (int i = 0; i < legendListeners.size(); i++) {
478
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
479
                }
480
                // getLayers().moveTo(0,0);
481
        }
482

    
483
        /**
484
         * <p>Fires a layer drawing event to all {@link LayerDrawingListener LayerDrawingListener} listeners registered,
485
         *  distinguishing the kind of event.</p>
486
         * 
487
         * @param e the event
488
         *
489
         * @see #addLayerDrawingListener(LayerDrawingListener)
490
         * @see #removeLayerDrawListener(LayerDrawingListener)
491
         */
492
        public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
493
                for (int i = 0; i < layerDrawingListeners.size(); i++)
494
                {
495
                        LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
496
                        switch (e.getEventType())
497
                        {
498
                                case LayerDrawEvent.LAYER_BEFORE_DRAW:
499
                                        listener.beforeLayerDraw(e);
500
                                        break;
501
                                case LayerDrawEvent.LAYER_AFTER_DRAW:
502
                                        listener.afterLayerDraw(e);
503
                                        break;
504
                                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
505
                                        listener.beforeGraphicLayerDraw(e);
506
                                        break;
507
                                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
508
                                        listener.afterLayerGraphicDraw(e);
509
                                        break;
510
                        }
511
                }
512
                // getLayers().moveTo(0,0);
513
        }
514

    
515
        /**
516
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
517
         * 
518
         * @param e the event with information of the error
519
         * 
520
         * @see #addErrorListener(ErrorListener)
521
         * @see #removeErrorListener(LegendListener)
522
         * @see #reportDriverExceptions(String, List)
523
         */
524
        public synchronized void callNewErrorEvent(ErrorEvent e) {
525
                for (int i = 0; i < errorListeners.size(); i++) {
526
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
527
                }
528
                errorListeners.clear();
529
                // getLayers().moveTo(0,0);
530
        }
531

    
532
        /**
533
         * <p>Removes the specified layer listener from this map.</p>
534
         * 
535
         * @param listener the listener to remove
536
         * 
537
         * @see #addLayerListener(LegendListener)
538
         * @see #callLegendChanged()
539
         */
540
        public void removeLayerListener(LegendListener listener) {
541
                legendListeners.remove(listener);
542
        }
543

    
544
        // SUGERENCIA DE PABLO:
545
        // public void removeLegendListener(LegendListener listener) {
546
        //         legendListeners.remove(listener);
547
        // }
548

    
549
        /**
550
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
551
         *
552
         * @return the hierarchy of nodes of layers stored in this map
553
         */
554
        public FLayers getLayers() {
555
                return layers;
556
        }
557

    
558
        /**
559
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
560
         *
561
         * @param b image with an accessible buffer of image data
562
         */
563
        public void drawLabels(BufferedImage b) {
564
        }
565

    
566
        /**
567
         * @see #redraw()
568
         */
569
        public void invalidate() {
570
                if (getLayers().getLayersCount() > 0)
571
                        getLayers().moveTo(0, 0);
572
        }
573

    
574
        /**
575
         * <p>Prints the layers of this map using the {@link Graphics2D Graphics2D} argument, that usually is
576
         * the {@link Graphics Graphics} of the printer.</p>
577
         *
578
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
579
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
580
         * @param properties a set with the settings to be applied to a whole print job and to all the documents in the print job
581
         *
582
         * @throws ReadDriverException if fails reading with driver.
583
         * 
584
         * @see FLayers#print(Graphics2D, ViewPort, Cancellable, double, PrintRequestAttributeSet)
585
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
586
         */
587
        public void print(Graphics2D g, double scale, PrintRequestAttributeSet properties) throws ReadDriverException {
588
                RenderingHints renderHints = new RenderingHints(
589
                                RenderingHints.KEY_ANTIALIASING,
590
                                RenderingHints.VALUE_ANTIALIAS_ON);
591
                renderHints.put(RenderingHints.KEY_RENDERING,
592
                                RenderingHints.VALUE_RENDER_QUALITY);
593
                g.setRenderingHints(renderHints);
594

    
595
                Cancellable cancel = new Cancellable() {
596
                        public boolean isCanceled() {
597
                                return false;
598
                        }
599

    
600
                        public void setCanceled(boolean canceled) {
601
                                // No queremos que se pueda cancelar la impresi?n.
602

    
603
                        }
604
                };
605
                layers.print(g, viewPort, cancel, scale, properties);
606
                tracLayer.draw(null, g, viewPort, cancel, scale);
607
        }
608

    
609
        /**
610
         * <p>Returns a new <code>MapContext</code> instance with the information of the <code>vp</code> argument, and the layers of this map.</p>
611
         *
612
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
613
         *
614
         * @return a new <code>MapContext</code> instance projected by <code>vp</code>
615
         */
616
        public MapContext createNewFMap(ViewPort vp) {
617
                MapContext ret = new MapContext(vp);
618
                ret.layers = this.layers;
619

    
620
                return ret;
621
        }
622

    
623
        /**
624
         * <p>Creates a new independent <code>MapContext</code> instance, that has a clone of the layers and the view port of this one.</p>
625
         * <p>The new map will have the same data source drivers to avoid waste memory, and work faster.</p>
626
         *
627
         * @return the new <code>MapContext</code> instance
628
         *
629
         * @throws XMLException if fails cloning the view port or a layer
630
         * 
631
         * @see FLayer#cloneLayer()
632
         * @see ViewPort#cloneViewPort()
633
         */
634
        public MapContext cloneFMap() throws XMLException {
635
                ViewPort vp = getViewPort().cloneViewPort();
636
                FLayers antLayers = getLayers();
637
                MapContext ret = new MapContext(vp);
638
                FLayers aux = new FLayers();//(ret,null);
639
                aux.setMapContext(ret);
640
                for (int i=0; i < antLayers.getLayersCount(); i++)
641
                {
642
                        FLayer lyr = antLayers.getLayer(i);
643
                        try {
644
                                aux.addLayer(lyr.cloneLayer());
645
                        } catch (Exception e) {
646

    
647
                                // TODO Auto-generated catch block
648
                                e.printStackTrace();
649
                                throw new XMLException(e);
650
                        }
651
                }
652
                ret.layers = aux;
653
                return ret;
654

    
655
//                return createFromXML(getXMLEntity());
656

    
657
        }
658

    
659
        /**
660
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
661
         * 
662
         * @return the new map
663
         */
664
        public MapContext cloneToDraw() {
665
                ViewPort vp = getViewPort().cloneViewPort();
666
                MapContext mapContext=new MapContext(getLayers(),vp);
667
                return mapContext;
668
        }
669

    
670
        /**
671
         * A?ade la capa que se pasa como par?metro al nodo que se pasa como
672
         * parametro y lanza ProjectionMismatchException si no est?n todas las capas
673
         * de este FMap en la misma proyecci?n. Lanza un ChildNotAllowedException si
674
         * la capa no es un FLayers y no permite hijos
675
         *
676
         * @param vectorial
677
         *            DOCUMENT ME!
678
         */
679

    
680
        /*
681
         * public void addLayer(LayerPath parent, FLayer layer) throws
682
         * ProjectionMismatchException, ChildrenNotAllowedException {
683
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
684
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
685
         */
686

    
687
        /**
688
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
689
         *
690
         * @param vectorial the layer to add
691
         */
692
        public void addToTrackLayer(FLayer vectorial) {
693
        }
694

    
695
        /**
696
         * <p>Returns the scale of the view in the screen.</p>
697
         *
698
         * @return one of this values:
699
         * <ul>
700
         * <li>the scale of the adjusted extent scale of the view in the screen
701
         * <li><code>-1</code> if there is no image
702
         * <li><code>0</code> if there is no extent defined for the image
703
         * </ul>
704
         * 
705
         * @see #setScaleView(long)
706
         * @see ViewPort#getAdjustedExtent()
707
         * @see IProjection#getScale(double, double, double, double)
708
         */
709
        public long getScaleView() {
710
                double dpi = getScreenDPI();
711
                IProjection proj = viewPort.getProjection();
712

    
713
                if (viewPort.getImageSize() == null)
714
                        return -1;
715

    
716
                if (viewPort.getAdjustedExtent() == null) {
717
                        return 0;
718
                }
719

    
720
                if (proj == null) {
721
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
722
                        return (long) (viewPort.getAdjustedExtent().getWidth() / w * CHANGEM[getViewPort()
723
                                        .getMapUnits()]);
724
                }
725

    
726
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*CHANGEM[getViewPort().getMapUnits()]),
727
                                (viewPort.getAdjustedExtent().getMaxX()*CHANGEM[getViewPort().getMapUnits()]), viewPort.getImageSize()
728
                                                .getWidth(), dpi));
729

    
730
        }
731

    
732
        /**
733
         * <p>Sets the new extent of the view, calculated using the scale argument.</p>
734
         * <p>Doesn't updates the scale if there isn't information about the dimension of the image or the
735
         *  adjusted extent.</p>
736
         *
737
         * @param scale the new scale for the view
738
         * 
739
         * @see ViewPort#setProjection(IProjection)
740
         * @see #getScaleView()
741
         */
742
        public void setScaleView(long scale) {
743
                clearAllCachingImageDrawnLayers();
744
                double dpi = getScreenDPI();
745
                if (viewPort.getImageSize() == null)
746
                        return;
747
                IProjection proj = viewPort.getProjection();
748
                if (viewPort.getAdjustedExtent() == null) {
749
                        return;
750
                }
751
                Rectangle2D rec=proj.getExtent(viewPort.getAdjustedExtent(),scale,viewPort.getImageWidth(),viewPort.getImageHeight(),CHANGE[getViewPort().getMapUnits()],CHANGEM[getViewPort().getDistanceUnits()],dpi);
752
                getViewPort().setExtent(rec);
753
        }
754

    
755
        /**
756
         * <p>Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
757
         * by default as it is defined in the default Toolkit.</p>
758
         * 
759
         * @return double with the screen's dpi
760
         */
761
        public static double getScreenDPI() {
762
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
763
                Toolkit kit = Toolkit.getDefaultToolkit();
764
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
765
                return dpi;
766
        }
767

    
768
        /**
769
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
770
         */
771
        public void setVectorial(VectorialAdapter v) {
772
        }
773

    
774
        /**
775
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
776
         */
777
        public void process(FeatureVisitor visitor) {
778
        }
779

    
780
        /**
781
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
782
         */
783
        public void processSelected(FeatureVisitor visitor) {
784
        }
785

    
786
        /**
787
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
788
         *      VectorialSubSet)
789
         */
790
        public void select(FeatureVisitor visitor) {
791
        }
792

    
793
        /**
794
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectFromSelection()
795
         */
796
        public void selectFromSelection() {
797
        }
798

    
799
        /**
800
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#createIndex()
801
         */
802
        public void createIndex() {
803
        }
804

    
805
        /**
806
         * @see org.cresques.geo.Projected#getProjection()
807
         *
808
         * @see ViewPort#getProjection()
809
         * @see #setProjection(IProjection)
810
         * @see #reProject(ICoordTrans)
811
         */
812
        public IProjection getProjection() {
813
                return getViewPort().getProjection();
814
        }
815

    
816
        /**
817
         * <p>Sets the new projection.</p>
818
         *
819
         * @param proj the new projection
820
         *
821
         * @see #getProjection()
822
         * @see ViewPort#setProjection(IProjection)
823
         * @see #reProject(ICoordTrans)
824
         */
825
        public void setProjection(IProjection proj) {
826
                if (getViewPort() != null) {
827
                        getViewPort().setProjection(proj);
828
                }
829
        }
830

    
831
        /**
832
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
833
         */
834
        public void reProject(ICoordTrans arg0) {
835
                // TODO implementar reprojecci?n (lo que sea eso)
836
        }
837

    
838
        /**
839
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByPoint(java.awt.geom.Point2D,
840
         *      double)
841
         */
842
        /*
843
         * public void selectByPoint(Point2D p, double tolerance) throws
844
         * DriverException { Point2D mapPoint = viewPort.toMapPoint((int) p.getX(),
845
         * (int) p.getY()); SelectByPointVisitor visitor = new
846
         * SelectByPointVisitor(); visitor.setQueriedPoint(mapPoint);
847
         * visitor.setTolerance(getViewPort().toMapDistance(3));
848
         *
849
         * try { layers.process(visitor); } catch (VisitException e) { throw new
850
         * RuntimeException("No se espera que SelectByPointVisitor lance esta
851
         * excepci?n", e); } }
852
         */
853

    
854
        /**
855
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByRect(java.awt.geom.Rectangle2D)
856
         */
857
        /*
858
         * public void selectByRect(Rectangle2D rect) throws DriverException {
859
         * FLayer[] actives = layers.getActives(); for (int i=0; i < actives.length;
860
         * i++) { if (actives[i] instanceof FLyrVect) { FLyrVect lyrVect =
861
         * (FLyrVect) actives[i]; FBitSet oldBitSet = lyrVect.getSelection();
862
         * FBitSet newBitSet = lyrVect.queryByRect(rect); newBitSet.xor(oldBitSet);
863
         * lyrVect.setSelection(newBitSet); } }
864
         *  }
865
         */
866

    
867
        /**
868
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
869
         *      int)
870
         */
871
        public void selectByShape(IGeometry g, int relationship) {
872
        }
873

    
874
        /**
875
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByPoint(Point2D,
876
         *      double)
877
         */
878
        public Record[] queryByPoint(Point2D p, double tolerance) {
879
                return null;
880
        }
881

    
882
        /**
883
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByRect(java.awt.geom.Rectangle2D)
884
         */
885
        public Record[] queryByRect(Rectangle2D rect) {
886
                return null;
887
        }
888

    
889
        /**
890
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
891
         *      int)
892
         */
893
        public Record[] queryByShape(IGeometry g, int relationship) {
894
                return null;
895
        }
896

    
897
        /**
898
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#getSelectionBounds()
899
         *
900
         * @see SelectedZoomVisitor#getSelectBound()
901
         */
902
        public Rectangle2D getSelectionBounds() {
903
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
904

    
905
                try {
906
                        layers.process(visitor);
907
                } catch (ReadDriverException e1) {
908
                        throw new RuntimeException(
909
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
910
                                        e1);
911
                } catch (VisitorException e) {
912
                        throw new RuntimeException(
913
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
914
                                        e);
915
                }
916

    
917
                return visitor.getSelectBound();
918
        }
919

    
920
        /**
921
         * <p>Draws this map if its {@link ViewPort ViewPort} has an extent defined:<br>
922
         * <ol>
923
         * <li>Selects only the layers that have to be drawn: {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
924
         * <li>Sets quality: antialiasing by text and images, and quality rendering. 
925
         * <li>Draws the layers.
926
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
927
         * <li>Draws the graphic layer.
928
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
929
         * <li>Invokes the garbage collector and memory clean.
930
         * </ol></p>
931
         * 
932
         * @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.
933
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
934
         * @param cancel shared object that determines if this layer can continue being drawn
935
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
936
         * @throws ReadDriverException if fails reading with the driver.
937
         */
938
        public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
939
                        double scale) throws ReadDriverException {
940
                if (viewPort.getExtent() == null) {
941
                        // System.err.println("viewPort.getExtent() = null");
942
                        return;
943
                }
944
                System.out.println("Viewport despues: " + viewPort.toString());
945
                /*
946
                 * if ((viewPort.getImageWidth() <=0) || (viewPort.getImageHeight() <=
947
                 * 0)) { return; }
948
                 */
949

    
950
                prepareDrawing(image, g, scale);
951

    
952
                // M?s c?lidad al texto
953
                RenderingHints renderHints = new RenderingHints(
954
                                RenderingHints.KEY_ANTIALIASING,
955
                                RenderingHints.VALUE_ANTIALIAS_ON);
956
                renderHints.put(RenderingHints.KEY_RENDERING,
957
                                RenderingHints.VALUE_RENDER_QUALITY);
958
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
959
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
960
                g.setRenderingHints(renderHints);
961

    
962
                long t1 = System.currentTimeMillis();
963
                layers.draw(image, g, viewPort, cancel, scale);
964

    
965
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
966
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
967
                fireLayerDrawingEvent(beforeTracLayerEvent);
968
                tracLayer.draw(image, g, viewPort, cancel, scale);
969
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
970
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
971
                fireLayerDrawingEvent(afterTracLayerEvent);
972

    
973
                //layers.setDirty(false);
974
                long t2 = System.currentTimeMillis();
975
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
976
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
977
                /*
978
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
979
                 * GeneralPath(viewPort.getExtent());
980
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
981
                 */
982
                System.gc();
983
        }
984
        
985
        /**
986
         * <p>Checks out layers that need to be repainted.</p>
987
         * <p>If one layer node uses a cache with previous image drawn, but hasn't any, or if it's dirty, then,
988
         *  that layer must be repainted. </p>
989
         * <p>If one layer node needn't to be repainted, checks out it recursively.</p>
990
         * <p><i>The cache of "previous image drawn" allows accelerate the repaint process.</i></p>
991
         * 
992
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
993
         * @see #recursivePrepareDrawing(FLayers, int)
994
         */
995
        private void prepareDrawing(BufferedImage image, Graphics2D g, double scale) {
996

    
997
                // Primera pasada: si alguna capa necesita repintarse por debajo
998
                // de la que tiene la cache, TODAS necesitan
999
                // ser repintadas.
1000
                boolean bNeedRepaint = false;
1001
                boolean bMayExistAceleration = false;
1002
                for (int i = 0; i < layers.getLayersCount(); i++)
1003
                {
1004
                        FLayer lyr = layers.getLayer(i);
1005
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1006
                        {
1007
                                bMayExistAceleration = true;
1008
                        }
1009
                        else
1010
                        {
1011
                                if (lyr.isDirty())
1012
                                        bNeedRepaint = true;
1013
                        }
1014
                }
1015
                if (bMayExistAceleration==false)
1016
                        bNeedRepaint = true;
1017
                if (bNeedRepaint)
1018
                        layers.setDirty(true);
1019
                else
1020
                        recursivePrepareDrawing(layers);
1021
        }
1022

    
1023
        /**
1024
         * <p>Invoked by {@linkplain MapContext#recursivePrepareDrawing(FLayers, int)}. Sets all previous layer nodes
1025
         *  in the argument as not dirty, what means that don't need to be repainted.</p>
1026
         * <p>Each layer node can decide validate or not it's sub-layers according its implementation.</p>
1027
         * <p>This is useful when it's editing, for accelerate the draw, because the layers of a {@link FLayers FLayers} node
1028
         * are painted according its index in the collection, and each one upper the previous.</p>
1029
         * 
1030
         * @param layers a node with layers 
1031
         * @param index index of the current layer in the internal list of layers
1032
         * 
1033
         * @see #recursivePrepareDrawing(FLayers, int)
1034
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1035
         */
1036
        private void validatePreviousLayers(FLayers layers, int index)
1037
        {
1038
                // TODO: Aqu? quiz?s habr?a que explorar los padres de las capas
1039
                // para marcar y/o asignar la imagen cacheada.
1040
                for (int i = 0; i < index; i++)
1041
                {
1042
                        FLayer lyr = layers.getLayer(i);
1043
                        lyr.setDirty(false);
1044
                }
1045
                // Las de arriba las marcamos como sucias
1046
//                for (int i = index; i < layers.getLayersCount(); i++)
1047
//                {
1048
//                        FLayer lyr = layers.getLayer(i);
1049
//                        lyr.setDirty(true);
1050
//                }
1051
        }
1052

    
1053
        /**
1054
         * <p>Checks out recursively, layers that have a cache with an image of previous layers drawn, and if are dirty
1055
         * notify the previous layers that are valid.</p>
1056
         * 
1057
         * @param parent node that contains the layers
1058
         * @param indexInParent the least layer index in the <code>parent</code> argument. This allows reduce the time processing
1059
         *  if is known that the first layers aren't a collection and will (or won't) be validated
1060
         * 
1061
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1062
         */
1063
        private void recursivePrepareDrawing(FLayers parent)
1064
        {
1065
                for (int i = 0; i < parent.getLayersCount(); i++)
1066
                {
1067
                        FLayer lyr = parent.getLayer(i);
1068
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1069
                        {
1070
                                // les decimos a las anteriores que est?n validadas (not dirty)
1071
                                // para que no se dibujen.
1072
                                if (lyr.isDirty())
1073
                                        validatePreviousLayers(parent, i);
1074
                        }
1075

    
1076
                        if (lyr instanceof FLayers)
1077
                        {
1078
                                recursivePrepareDrawing((FLayers)lyr);
1079
                        }
1080
                }
1081

    
1082
        }
1083

    
1084
        /**
1085
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1086
         * 
1087
         * @param image image used to accelerate the screen draw
1088
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1089
         * @param cancel shared object that determines if this layer can continue being drawn
1090
         * @param scale value that represents the scale
1091
         * @throws ReadDriverException if fails reading with the driver.
1092
         * 
1093
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1094
         */
1095
        public void drawGraphics(BufferedImage image, Graphics2D g,
1096
                        Cancellable cancel, double scale) throws ReadDriverException {
1097
                if (viewPort == null)
1098
                        return;
1099
                tracLayer.draw(image, g, viewPort, cancel, scale);
1100
        }
1101

    
1102
        /**
1103
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1104
         *  the task as cancellable.</p>
1105
         * 
1106
         * @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.
1107
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1108
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1109
         * 
1110
         * @throws ReadDriverException if the driver fails reading.
1111
         * 
1112
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1113
         */
1114
        public void draw(BufferedImage image, Graphics2D g, double scale)
1115
                        throws ReadDriverException {
1116
                layers.setDirty(true);
1117
                draw(image, g, new Cancellable() {
1118
                        /**
1119
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1120
                         */
1121
                        public boolean isCanceled() {
1122
                                return false;
1123
                        }
1124

    
1125
                        public void setCanceled(boolean canceled) {
1126
                                // TODO Auto-generated method stub
1127

    
1128
                        }
1129
                }, scale);
1130
        }
1131

    
1132
        /**
1133
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1134
         *
1135
         * @return the view port
1136
         * 
1137
         * @see #setViewPort(ViewPort)
1138
         */
1139
        public ViewPort getViewPort() {
1140
                return viewPort;
1141
        }
1142

    
1143
        /**
1144
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1145
         *  of this map.</p>
1146
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1147
         *  adds the new one.</p>
1148
         *
1149
         * @param viewPort the viewPort
1150
         * 
1151
         * @see #getViewPort()
1152
         */
1153
        public void setViewPort(ViewPort viewPort) {
1154
                if (this.viewPort != null) {
1155
                        this.viewPort.removeViewPortListener(eventBuffer);
1156
                }
1157

    
1158
                this.viewPort = viewPort;
1159
                if (viewPort != null)
1160
                        viewPort.addViewPortListener(eventBuffer);
1161
        }
1162

    
1163
        /**
1164
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1165
         *
1166
         * @param extent the extent of the new zoom
1167
         */
1168
        public void zoomToExtent(Rectangle2D extent) {
1169
                if (extent!=null)
1170
                        getViewPort().setExtent(extent);
1171
        }
1172

    
1173
        /**
1174
         * <p>Returns the union of all extents of all layers of this map.</p>
1175
         *
1176
         * @return full extent of layers of this map
1177
         * @throws ReadDriverException if the driver fails reading.
1178
         * 
1179
         * @see FLayers#getFullExtent()
1180
         */
1181
        public Rectangle2D getFullExtent() throws ReadDriverException {
1182
                return layers.getFullExtent();
1183
        }
1184

    
1185
        /**
1186
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1187
         * <ul>
1188
         * <li>XML entity of the internal {@link ViewPort ViewPort}.
1189
         * <li>XML entity of the internal {@link FLayers FLayers}.
1190
         * </ul>
1191
         * 
1192
         * @return XMLEntity the XML entity
1193
         * @throws XMLException if there is any error creating the XML from the map.
1194
         * 
1195
         * @see #createFromXML(XMLEntity)
1196
         * @see #createFromXML03(XMLEntity)
1197
         * @see ViewPort#getXMLEntity()
1198
         * @see FLayers#getXMLEntity()
1199
         */
1200
        public XMLEntity getXMLEntity() throws XMLException {
1201
                XMLEntity xml = new XMLEntity();
1202
                xml.putProperty("className", this.getClass().getName());
1203
                xml.addChild(viewPort.getXMLEntity());
1204
                xml.addChild(layers.getXMLEntity());
1205

    
1206
                return xml;
1207
        }
1208

    
1209
        /**
1210
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1211
         *  the data of the {@link ViewPort ViewPort} and
1212
         *  {@link FLayers FLayers}.</p>
1213
         *
1214
         * @param xml an XML entity
1215
         *
1216
         * @return the new <code>MapContext</code> instance
1217
         *
1218
         * @throws XMLException if there is any error creating the map from the XML.
1219
         * 
1220
         * @see #getXMLEntity()
1221
         * @see #createFromXML(XMLEntity)
1222
         * @see ViewPort#createFromXML03(XMLEntity)
1223
         * @see FLayers#setXMLEntity03(XMLEntity)
1224
         */
1225
        public static MapContext createFromXML03(XMLEntity xml) throws XMLException {
1226
                ViewPort vp = ViewPort.createFromXML03(xml.getChild(0));
1227
                MapContext fmap = new MapContext(vp);
1228
                fmap.layers.setXMLEntity03(xml.getChild(1));
1229

    
1230
                return fmap;
1231
        }
1232

    
1233
        /**
1234
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1235
         *  with the data of the {@link ViewPort ViewPort} and
1236
         *  {@link FLayers FLayers}.</p>
1237
         *
1238
         * @param xml an XML entity
1239
         *
1240
         * @return the new <code>MapContext</code> instance
1241
         *
1242
         * @throws XMLException if there is any error creating the map from the XML.
1243
         * 
1244
         * @see #getXMLEntity()
1245
         * @see #createFromXML03(XMLEntity)
1246
         * @see ViewPort#createFromXML(XMLEntity)
1247
         * @see FLayers#setXMLEntity(XMLEntity)
1248
         */
1249
        public static MapContext createFromXML(XMLEntity xml) throws XMLException {
1250
                ViewPort vp = ViewPort.createFromXML(xml.getChild(0));
1251
                MapContext fmap = new MapContext(vp);
1252
                fmap.layers.setXMLEntity(xml.getChild(1));
1253
                fmap.layers.setName("root layer");
1254
                return fmap;
1255
        }
1256

    
1257
        /**
1258
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1259
         *
1260
         * @param listener the new listener
1261
         *
1262
         * @return <code>true</code> if has added the listener successfully
1263
         * 
1264
         * @see #removeAtomicEventListener(AtomicEventListener) 
1265
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1266
         */
1267
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1268
                return eventBuffer.addAtomicEventListener(listener);
1269
        }
1270

    
1271
        /**
1272
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1273
         *
1274
         * @param listener the listener to remove
1275
         * 
1276
     * @return <tt>true</tt> if the list contained the specified element
1277
         * 
1278
         * @see #addAtomicEventListener(AtomicEventListener)
1279
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1280
         */
1281
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1282
                return eventBuffer.removeAtomicEventListener(listener);
1283
        }
1284

    
1285
        /**
1286
         * @see EventBuffer#beginAtomicEvent()
1287
         * 
1288
         * @see #endAtomicEvent()
1289
         */
1290
        public void beginAtomicEvent() {
1291
                eventBuffer.beginAtomicEvent();
1292
        }
1293

    
1294
        /**
1295
         * @see EventBuffer#endAtomicEvent()
1296
         * 
1297
         * @see #beginAtomicEvent()
1298
         */
1299
        public void endAtomicEvent() {
1300
                eventBuffer.endAtomicEvent();
1301
        }
1302

    
1303
        /**
1304
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1305
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1306
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1307
         *
1308
         * @author Fernando Gonz?lez Cort?s
1309
         */
1310
        public class LayerEventListener implements LayerCollectionListener {
1311
                /*
1312
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1313
                 */
1314
                public void layerAdded(LayerCollectionEvent e) {
1315
                        // Si es la primera capa, fijamos su extent al ViewPort
1316
                        // if (getLayers().getLayersCount() == 1) {
1317
                        if (getViewPort().getExtent() == null) {
1318
                                FLayer lyr = e.getAffectedLayer();
1319
                                if (lyr.isAvailable()) {
1320
                                        try {
1321
                                                getViewPort().setExtent(lyr.getFullExtent());
1322
                                        } catch (ReadDriverException e1) {
1323
                                                e1.printStackTrace();
1324
                                        }
1325
                                }
1326
                        }
1327

    
1328
                        // Registramos al FMap como listener del legend de las capas
1329
                        FLayer lyr = e.getAffectedLayer();
1330
                        selectionListener(lyr);
1331
                }
1332

    
1333
                /**
1334
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1335
                 *
1336
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1337
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1338
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one, 
1339
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p> 
1340
                 * 
1341
                 * @param the layer or layers
1342
                 */
1343
                private void selectionListener(FLayer lyr){
1344
                        lyr.addLayerListener(eventBuffer);
1345

    
1346
                        if (lyr instanceof Classifiable) {
1347
                                Classifiable c = (Classifiable) lyr;
1348
                                c.addLegendListener(eventBuffer);
1349
                        }
1350

    
1351
                        if (lyr instanceof AlphanumericData) {
1352
                                Selectable s=null;
1353
                                try {
1354
                                        s = ((AlphanumericData) lyr).getRecordset();
1355
                                        if (s!=null) {
1356
                                                s.addSelectionListener(eventBuffer);
1357
                                        }
1358
                                } catch (ReadDriverException e1) {
1359
                                        e1.printStackTrace();
1360
                                }
1361

    
1362
                        }
1363
                        if (lyr instanceof FLayers){
1364
                                FLayers lyrs=(FLayers)lyr;
1365
                                for(int i=0;i<lyrs.getLayersCount();i++){
1366
                                        selectionListener(lyrs.getLayer(i));
1367
                                }
1368
                        }
1369

    
1370
                }
1371
                /*
1372
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1373
                 */
1374
                public void layerMoved(LayerPositionEvent e) {
1375
                }
1376

    
1377
                /*
1378
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1379
                 */
1380
                public void layerRemoved(LayerCollectionEvent e) {
1381
                        FLayer lyr = e.getAffectedLayer();
1382

    
1383
                        lyr.removeLayerListener(eventBuffer);
1384

    
1385
                        if (lyr instanceof Classifiable) {
1386
                                Classifiable c = (Classifiable) lyr;
1387
                                c.removeLegendListener(eventBuffer);
1388
                        }
1389

    
1390
                        if (lyr instanceof Selectable) {
1391
                                Selectable s = (Selectable) lyr;
1392
                                s.addSelectionListener(eventBuffer);
1393
                        }
1394
                }
1395

    
1396
                /*
1397
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1398
                 */
1399
                public void layerAdding(LayerCollectionEvent e)
1400
                                throws CancelationException {
1401
                }
1402

    
1403
                /*
1404
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1405
                 */
1406
                public void layerMoving(LayerPositionEvent e)
1407
                                throws CancelationException {
1408
                }
1409

    
1410
                /*
1411
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1412
                 */
1413
                public void layerRemoving(LayerCollectionEvent e)
1414
                                throws CancelationException {
1415
                }
1416

    
1417

    
1418
                /*
1419
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1420
                 */
1421
                public void visibilityChanged(LayerCollectionEvent e)
1422
                                throws CancelationException {
1423
                }
1424
        }
1425

    
1426
        /**
1427
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the 
1428
         *  collection of layers argument.</p> 
1429
         * 
1430
         * @param a collection of layers
1431
         */
1432
        public void addAsCollectionListener(FLayers layers2) {
1433
                layers2.addLayerCollectionListener(layerEventListener);
1434
        }
1435

    
1436
        /**
1437
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1438
         * 
1439
         * @return the graphic layer of this map
1440
         * 
1441
         * @see #setGraphicsLayer(GraphicLayer)
1442
         */
1443
        public GraphicLayer getGraphicsLayer() {
1444
                return tracLayer;
1445
        }
1446

    
1447
        /**
1448
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1449
         * 
1450
         * @param graphicLayer the new graphic layer
1451
         * 
1452
         * @see #getGraphicsLayer()
1453
         */
1454
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1455
                tracLayer = graphicLayer;
1456
        }
1457

    
1458
        /**
1459
         * <p>Indicates whether some other object is "equal to" this map.</p>
1460
         * <p>Returns <code>true</code> if success one of this options:
1461
         * <ul>
1462
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1463
         * <li>Both maps have the same layers.
1464
         * <li>Both maps have the same number of layers and with the same name.
1465
         * </ul>
1466
         * </p>
1467
         * 
1468
         * @param obj the reference object with which to compare.
1469
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1470
         *  
1471
         * @see Object#equals(Object)
1472
         */
1473
        public boolean equals(Object arg0) {
1474
                MapContext map = (MapContext) arg0;
1475
                if (super.equals(arg0))
1476
                        return true;
1477
                if (getLayers() == map.getLayers())
1478
                        return true;
1479
                boolean isEqual = true;
1480
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1481
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1482

    
1483
                                if (!getLayers().getLayer(i).getName().equals(
1484
                                                map.getLayers().getLayer(i).getName())) {
1485
                                        isEqual = false;
1486
                                }
1487

    
1488
                        }
1489
                } else {
1490
                        isEqual = false;
1491
                }
1492
                return isEqual;
1493
        }
1494

    
1495
        /**
1496
         * <p>Registers the message of an error associated to this map.</p>
1497
         * 
1498
         * @param stringProperty the error message
1499
         * 
1500
         * @see #getLayersError()
1501
         * @see #clearErrors()
1502
         */
1503
        public void addLayerError(String stringProperty) {
1504
                layersError.add(stringProperty);
1505
        }
1506

    
1507
        /**
1508
         * <p>Gets the list with all error messages registered to this map.</p>
1509
         * 
1510
         * @return the list of errors registered to this map
1511
         * 
1512
         * @see #addLayerError(String)
1513
         * @see #clearErrors()
1514
         */
1515
        public ArrayList getLayersError() {
1516
                return layersError;
1517
        }
1518

    
1519
        /**
1520
         * <p>Removes all error messages associated to this map.</p>
1521
         * 
1522
         * @see #addLayerError(String)
1523
         * @see #getLayersError()
1524
         */
1525
        public void clearErrors() {
1526
                layersError.clear();
1527
        }
1528

    
1529
        /**
1530
         * <p>Removes from this map, all caching images of drawn layers, registered.</p>
1531
         * 
1532
         * @see #clearCachingImageDrawnLayers(FLayers)
1533
         */
1534
        public void clearAllCachingImageDrawnLayers() {
1535
                clearCachingImageDrawnLayers(this.layers);
1536
        }
1537
        
1538
        /**
1539
         * <p>Removes from the layer collection argument, all caching images of drawn layers, registered.</p>
1540
         * 
1541
         * @param layers a layer collection
1542
         * 
1543
         * @see #clearAllCachingImageDrawnLayers()
1544
         */
1545
        private void clearCachingImageDrawnLayers(FLayers layers){
1546
                int i;
1547
                FLayer layer;
1548
                for (i=0;i< layers.getLayersCount();i++){
1549
                        layer = layers.getLayer(i);
1550
                        if (layer instanceof FLayers) {
1551
                                clearCachingImageDrawnLayers((FLayers)layer);
1552
                        } else {
1553
                                layer.setCacheImageDrawnLayers(null);
1554
                        }
1555
                }
1556
        }
1557

    
1558
        /**
1559
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1560
         * 
1561
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1562
         * @return the new layer node
1563
         */
1564
        public FLayers getNewGroupLayer(FLayers parent) {
1565
                FLayers group1 = new FLayers();//(this,parent);
1566
                group1.setMapContext(this);
1567
                group1.setParentLayer(parent);
1568
            return group1;
1569
        }
1570
}