Statistics
| Revision:

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

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

    
54
import javax.print.attribute.PrintRequestAttributeSet;
55

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

    
60
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
61
import com.hardcode.gdbms.engine.data.driver.DriverException;
62
import com.iver.cit.gvsig.exceptions.expansionfile.ExpansionFileReadException;
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.core.symbols.ISymbol;
66
import com.iver.cit.gvsig.fmap.layers.CancelationException;
67
import com.iver.cit.gvsig.fmap.layers.FLayer;
68
import com.iver.cit.gvsig.fmap.layers.FLayers;
69
import com.iver.cit.gvsig.fmap.layers.GraphicLayer;
70
import com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent;
71
import com.iver.cit.gvsig.fmap.layers.LayerCollectionListener;
72
import com.iver.cit.gvsig.fmap.layers.LayerDrawEvent;
73
import com.iver.cit.gvsig.fmap.layers.LayerDrawingListener;
74
import com.iver.cit.gvsig.fmap.layers.LayerPositionEvent;
75
import com.iver.cit.gvsig.fmap.layers.LegendListener;
76
import com.iver.cit.gvsig.fmap.layers.VectorialAdapter;
77
import com.iver.cit.gvsig.fmap.layers.XMLException;
78
import com.iver.cit.gvsig.fmap.layers.layerOperations.AlphanumericData;
79
import com.iver.cit.gvsig.fmap.layers.layerOperations.Classifiable;
80
import com.iver.cit.gvsig.fmap.layers.layerOperations.Selectable;
81
import com.iver.cit.gvsig.fmap.operations.selection.Record;
82
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
83
import com.iver.cit.gvsig.fmap.operations.strategies.SelectedZoomVisitor;
84
import com.iver.utiles.XMLEntity;
85
import com.iver.utiles.swing.threads.Cancellable;
86

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

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

    
181
        /* Do not alter the order and the values of this array, if you need append values.*/
182
        public static final String[] NAMES= {
183
                Messages.getString("Kilometros"),
184
                Messages.getString("Metros"),
185
                Messages.getString("Centimetros"),
186
                Messages.getString("Milimetros"),
187
                Messages.getString("Millas"),
188
                Messages.getString("Yardas"),
189
                Messages.getString("Pies"),
190
                Messages.getString("Pulgadas"),
191
                Messages.getString("Grados"),
192
        };
193

    
194
        public static final int EQUALS = 0;
195

    
196
        public static final int DISJOINT = 1;
197

    
198
        public static final int INTERSECTS = 2;
199

    
200
        public static final int TOUCHES = 3;
201

    
202
        public static final int CROSSES = 4;
203

    
204
        public static final int WITHIN = 5;
205

    
206
        public static final int CONTAINS = 6;
207

    
208
        public static final int OVERLAPS = 7;
209
        /**
210
         * A hierarchy of {@link FLayers FLayers} nodes.
211
         *
212
         * @see #getLayers()
213
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
214
         */
215
        protected FLayers layers;
216
        /**
217
         * A layer with graphical items: geometries and symbols.
218
         *
219
         * @see #getGraphicsLayer()
220
         * @see #setGraphicsLayer(GraphicLayer)
221
         * @see #drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
222
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
223
         */
224
        private GraphicLayer tracLayer = new GraphicLayer();
225
        /**
226
         * Information for draw layers in a view.
227
         *
228
         * @see #getViewPort()
229
         * @see #setViewPort(ViewPort)
230
         */
231
        private ViewPort viewPort;
232

    
233
        // private ArrayList invalidationListeners = new ArrayList();
234

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

    
244
        /**
245
         * Array list with all {@link LayerDrawingListener LayerDrawingListener} registered to this map.
246
         *
247
         * @see #addLayerDrawingListener(LayerDrawingListener)
248
         * @see #removeLayerDrawListener(LayerDrawingListener)
249
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
250
         */
251
        private ArrayList layerDrawingListeners = new ArrayList();
252
        /**
253
         * <p>Buffer that is used to store and eject events produced on this map:
254
         * <ul>
255
         *  <li>Layer collection events.
256
         *  <li>View port events.
257
         *  <li>Atomic events.
258
         *  <li>Layer events.
259
         *  <li>Legend events on a {@link Classificable Classificable} layer.
260
         *  <li>Selection events on an {@link AlphanumericData AlphanumericData} data layer.
261
         * </ul>
262
         * </p>
263
         *
264
         * @see #addAtomicEventListener(AtomicEventListener)
265
         * @see #removeAtomicEventListener(AtomicEventListener)
266
         * @see #beginAtomicEvent()
267
         * @see #endAtomicEvent()
268
         */
269
        private EventBuffer eventBuffer = new EventBuffer();
270
        /**
271
         * Event listener for the collection of layers of this map.
272
         */
273
        private LayerEventListener layerEventListener = null;
274
        /**
275
         * List with information of all errors produced on all layers.
276
         *
277
         * @see #addLayerError(String)
278
         * @see #getLayersError()
279
         * @see #clearErrors()
280
         */
281
        private ArrayList layersError = new ArrayList();
282
        /**
283
         * Array list with all {@link ErrorListener ErrorListener} registered to this map.
284
         *
285
         * @see #addErrorListener(ErrorListener)
286
         * @see #removeErrorListener(LegendListener)
287
         * @see #callNewErrorEvent(ErrorEvent)
288
         * @see #reportDriverExceptions(String, List)
289
         */
290
        private ArrayList errorListeners = new ArrayList();
291

    
292

    
293

    
294
        // public static ResourceBundle myResourceBundle =
295
        // ResourceBundle.getBundle("FMap");
296

    
297
        /**
298
         * <p>Default zoom in factor.</p>
299
         * <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
300
         * area but with the items bigger.</p>
301
         */
302
        public static double ZOOMINFACTOR=2;
303
        /**
304
         * <p>Default zoom out factor.</p>
305
         * <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
306
         * area but with the items smaller.</p>
307
         */
308
        public static double ZOOMOUTFACTOR=0.5;
309

    
310
        private static Color selectionColor = Color.YELLOW;
311

    
312

    
313
        /**
314
         * Devuelve el color que se aplica a los shapes seleccionados.
315
         *
316
         * @return DOCUMENT ME!
317
         */
318
        public static Color getSelectionColor() {
319
                return selectionColor;
320
        }
321

    
322
        /**
323
         * Introduce el color que se aplica a los shapes seleccionados.
324
         *
325
         * @param selectionColor DOCUMENT ME!
326
         */
327
        public static void setSelectionColor(Color selectionColor) {
328
                MapContext.selectionColor = selectionColor;
329
        }
330
        /**
331
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
332
         *  without layers.</p>
333
         *
334
         * @param vp information for drawing the layers of this map as a view
335
         */
336
        public MapContext(ViewPort vp) {
337
                this.layers = new FLayers(this, null);
338

    
339
                layerEventListener = new LayerEventListener();
340
                layers.addLayerCollectionListener(layerEventListener);
341
                layers.addLayerCollectionListener(eventBuffer);
342

    
343
                setViewPort(vp);
344

    
345
        }
346
        /**
347
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
348
         *
349
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
350
         * @param vp information for drawing the layers of this map as a view
351
         */
352
        public MapContext(FLayers fLayers, ViewPort vp) {
353
                this.layers = fLayers;
354

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

    
359
                setViewPort(vp);
360
        }
361

    
362
        /**
363
         * <p>Reports to all driver error listeners registered of a bundle of driver exceptions caused in the same map atomic transaction.</p>
364
         *
365
         * @param introductoryText introductory text specified by developer. If <code>null</code>, use ""
366
         * @param driverExceptions list with a bundle of driver exceptions caught during an atomic event
367
         *
368
         * @see #addErrorListener(ErrorListener)
369
         * @see #removeErrorListener(LegendListener)
370
         * @see #callNewErrorEvent(ErrorEvent)
371
         */
372
        public synchronized void reportDriverExceptions(String introductoryText,
373
                                                                                                        List driverExceptions){
374
                for (int i = 0; i < errorListeners.size(); i++) {
375
                        ((ErrorListener) errorListeners.get(i)).
376
                                reportDriverExceptions(introductoryText, driverExceptions);
377
                }
378
        }
379

    
380

    
381
        /**
382
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
383
         *
384
         * @param listener the legend listener
385
         *
386
         * @see #removeLayerListener(LegendListener)
387
         * @see #callLegendChanged()
388
         */
389
        public void addLayerListener(LegendListener listener) {
390
                if (!legendListeners.contains(listener))
391
                        legendListeners.add(listener);
392
        }
393
        // SUGERENCIA DE PABLO
394
        //        public void addLegendListener(LegendListener listener) {
395
        //                if (!legendListeners.contains(listener))
396
        //                        legendListeners.add(listener);
397
        //        }
398

    
399
        /**
400
         * <p>Adds the specified layer drawing listener to catch and handle drawing events from layers of this map.</p>
401
         *
402
         * @param listener the listener to add
403
         *
404
         * @see #removeLayerDrawListener(LayerDrawingListener)
405
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
406
         */
407
        public void addLayerDrawingListener(LayerDrawingListener listener) {
408
                layerDrawingListeners.add(listener);
409
        }
410
        /**
411
         * <p>Removes the specified layer drawing listener from this map.</p>
412
         *
413
         * @param listener the listener to remove
414
         *
415
         * @see #addLayerDrawingListener(LayerDrawingListener)
416
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
417
         */
418
        public void removeLayerDrawListener(LayerDrawingListener listener) {
419
                layerDrawingListeners.remove(listener);
420
        }
421
        /**
422
         * <p>Adds the specified error listener to receive error events from this map.</p>
423
         *
424
         * @param listener the listener to add
425
         *
426
         * @see #removeErrorListener(LegendListener)
427
         * @see #callNewErrorEvent(ErrorEvent)
428
         * @see #reportDriverExceptions(String, List)
429
         */
430
        public void addErrorListener(ErrorListener listener) {
431
                errorListeners.add(listener);
432
        }
433
        /**
434
         * <p>Removes the specified error listener from this map.</p>
435
         *
436
         * @param listener the listener to remove
437
         *
438
         * @see #addErrorListener(ErrorListener)
439
         * @see #callNewErrorEvent(ErrorEvent)
440
         * @see #reportDriverExceptions(String, List)
441
         */
442
        public void removeErrorListener(LegendListener listener) {
443
                legendListeners.remove(listener);
444
        }
445

    
446
        // SUGERENCIA DE PABLO:
447
        //public void removeErrorListener(ErrorListener listener) {
448
        //        errorListeners.remove(listener);
449
        //}
450

    
451
        /**
452
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
453
         * <p>This method must be call only if it's wanted to reflect a legend change.</p>
454
         *
455
         * @see #addLayerListener(LegendListener)
456
         * @see #removeLayerListener(LegendListener)
457
         */
458
        public synchronized void callLegendChanged() {
459
                for (int i = 0; i < legendListeners.size(); i++) {
460
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
461
                }
462
                // getLayers().moveTo(0,0);
463
        }
464
        /**
465
         * <p>Fires a layer drawing event to all {@link LayerDrawingListener LayerDrawingListener} listeners registered,
466
         *  distinguishing the kind of event.</p>
467
         *
468
         * @param e the event
469
         *
470
         * @see #addLayerDrawingListener(LayerDrawingListener)
471
         * @see #removeLayerDrawListener(LayerDrawingListener)
472
         */
473
        public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
474
                for (int i = 0; i < layerDrawingListeners.size(); i++)
475
                {
476
                        LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
477
                        switch (e.getEventType())
478
                        {
479
                                case LayerDrawEvent.LAYER_BEFORE_DRAW:
480
                                        listener.beforeLayerDraw(e);
481
                                        break;
482
                                case LayerDrawEvent.LAYER_AFTER_DRAW:
483
                                        listener.afterLayerDraw(e);
484
                                        break;
485
                                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
486
                                        listener.beforeGraphicLayerDraw(e);
487
                                        break;
488
                                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
489
                                        listener.afterLayerGraphicDraw(e);
490
                                        break;
491
                        }
492
                }
493
                // getLayers().moveTo(0,0);
494
        }
495
        /**
496
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
497
         *
498
         * @param e the event with information of the error
499
         *
500
         * @see #addErrorListener(ErrorListener)
501
         * @see #removeErrorListener(LegendListener)
502
         * @see #reportDriverExceptions(String, List)
503
         */
504
        public synchronized void callNewErrorEvent(ErrorEvent e) {
505
                for (int i = 0; i < errorListeners.size(); i++) {
506
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
507
                }
508
                errorListeners.clear();
509
                // getLayers().moveTo(0,0);
510
        }
511

    
512
        /**
513
         * <p>Removes the specified layer listener from this map.</p>
514
         *
515
         * @param listener the listener to remove
516
         *
517
         * @see #addLayerListener(LegendListener)
518
         * @see #callLegendChanged()
519
         */
520
        public void removeLayerListener(LegendListener listener) {
521
                legendListeners.remove(listener);
522
        }
523

    
524
        // SUGERENCIA DE PABLO:
525
        // public void removeLegendListener(LegendListener listener) {
526
        //         legendListeners.remove(listener);
527
        // }
528

    
529
        /**
530
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
531
         *
532
         * @return the hierarchy of nodes of layers stored in this map
533
         */
534
        public FLayers getLayers() {
535
                return layers;
536
        }
537

    
538

    
539
        /**
540
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
541
         *
542
         * @param b image with an accessible buffer of image data
543
         */
544
        public void drawLabels(BufferedImage b) {
545
        }
546

    
547
        /**
548
         * @see #redraw()
549
         */
550
        public void invalidate() {
551
                if (getLayers().getLayersCount() > 0)
552
                        getLayers().moveTo(0, 0);
553
        }
554

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

    
576
                Cancellable cancel = new Cancellable() {
577
                        public boolean isCanceled() {
578
                                return false;
579
                        }
580

    
581
                        public void setCanceled(boolean canceled) {
582
                                // No queremos que se pueda cancelar la impresi?n.
583

    
584
                        }
585
                };
586
                layers.print(g, viewPort, cancel, scale, properties);
587
                tracLayer.draw(null, g, viewPort, cancel, scale);
588
        }
589
        /**
590
         * <p>Returns a new map with the information of the <code>vp</code> argument, and the layers of this map.</p>
591
         *
592
         * @param vp information for drawing the layers
593
         *
594
         * @return a new map
595
         */
596
        public MapContext createNewFMap(ViewPort vp) {
597
                MapContext ret = new MapContext(vp);
598
                ret.layers = this.layers;
599

    
600
                return ret;
601
        }
602

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

    
626
                                // TODO Auto-generated catch block
627
                                e.printStackTrace();
628
                                throw new XMLException(e);
629
                        }
630
                }
631
                ret.layers = aux;
632
                return ret;
633

    
634
//                return createFromXML(getXMLEntity());
635

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

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

    
658
        /*
659
         * public void addLayer(LayerPath parent, FLayer layer) throws
660
         * ProjectionMismatchException, ChildrenNotAllowedException {
661
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
662
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
663
         */
664

    
665
        /**
666
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
667
         *
668
         * @param vectorial the layer to add
669
         */
670
        public void addToTrackLayer(FLayer vectorial) {
671
        }
672

    
673
        /**
674
         * <p>Returns the scale of the view in the screen.</p>
675
         *
676
         * @return one of this values:
677
         * <ul>
678
         * <li>the scale of the adjusted extent scale of the view in the screen
679
         * <li><code>-1</code> if there is no image
680
         * <li><code>0</code> if there is no extent defined for the image
681
         * </ul>
682
         *
683
         * @see #setScaleView(long)
684
         * @see ViewPort#getAdjustedExtent()
685
         * @see IProjection#getScale(double, double, double, double)
686
         */
687
        public long getScaleView() {
688
                double dpi = getScreenDPI();
689
                IProjection proj = viewPort.getProjection();
690

    
691
                if (viewPort.getImageSize() == null)
692
                        return -1;
693

    
694
                if (viewPort.getAdjustedExtent() == null) {
695
                        return 0;
696
                }
697

    
698
                if (proj == null) {
699
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
700
                        return (long) (viewPort.getAdjustedExtent().getWidth() / w * CHANGEM[getViewPort()
701
                                        .getMapUnits()]);
702
                }
703

    
704
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*CHANGEM[getViewPort().getMapUnits()]),
705
                                (viewPort.getAdjustedExtent().getMaxX()*CHANGEM[getViewPort().getMapUnits()]), viewPort.getImageSize()
706
                                                .getWidth(), dpi));
707

    
708
        }
709

    
710

    
711
        /**
712
         * <p>Sets the new extent of the view, calculated using the scale argument.</p>
713
         * <p>Doesn't updates the scale if there isn't information about the dimension of the image or the
714
         *  adjusted extent.</p>
715
         *
716
         * @param scale the new scale for the view
717
         *
718
         * @see ViewPort#setProjection(IProjection)
719
         * @see #getScaleView()
720
         */
721
        public void setScaleView(long scale) {
722
                clearAllCachingImageDrawnLayers();
723
                double dpi = getScreenDPI();
724
                if (viewPort.getImageSize() == null)
725
                        return;
726
                IProjection proj = viewPort.getProjection();
727
                if (viewPort.getAdjustedExtent() == null) {
728
                        return;
729
                }
730
                Rectangle2D rec=proj.getExtent(viewPort.getAdjustedExtent(),scale,viewPort.getImageWidth(),viewPort.getImageHeight(),CHANGE[getViewPort().getMapUnits()],CHANGEM[getViewPort().getDistanceUnits()],dpi);
731
                getViewPort().setExtent(rec);
732
        }
733

    
734
        /**
735
         * Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
736
         * by default as it is defined in the default Toolkit.
737
         * @return double with the screen's dpi
738
         */
739
        public static double getScreenDPI() {
740
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
741
                Toolkit kit = Toolkit.getDefaultToolkit();
742
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
743
                return dpi;
744
        }
745
        /**
746
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
747
         */
748
        public void setVectorial(VectorialAdapter v) {
749
        }
750

    
751
        /**
752
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
753
         */
754
        public void process(FeatureVisitor visitor) {
755
        }
756

    
757
        /**
758
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
759
         */
760
        public void processSelected(FeatureVisitor visitor) {
761
        }
762

    
763
        /**
764
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
765
         *      VectorialSubSet)
766
         */
767
        public void select(FeatureVisitor visitor) {
768
        }
769

    
770
        /**
771
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectFromSelection()
772
         */
773
        public void selectFromSelection() {
774
        }
775

    
776
        /**
777
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#createIndex()
778
         */
779
        public void createIndex() {
780
        }
781

    
782
        /**
783
         * @see org.cresques.geo.Projected#getProjection()
784
         *
785
         * @see ViewPort#getProjection()
786
         * @see #setProjection(IProjection)
787
         * @see #reProject(ICoordTrans)
788
         */
789
        public IProjection getProjection() {
790
                return getViewPort().getProjection();
791
        }
792

    
793
        /**
794
         * <p>Sets the new projection.</p>
795
         *
796
         * @param proj the new projection
797
         *
798
         * @see #getProjection()
799
         * @see ViewPort#setProjection(IProjection)
800
         * @see #reProject(ICoordTrans)
801
         */
802
        public void setProjection(IProjection proj) {
803
                if (getViewPort() != null) {
804
                        getViewPort().setProjection(proj);
805
                }
806
        }
807

    
808
        /*
809
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
810
         */
811
        public void reProject(ICoordTrans arg0) {
812
                // TODO implementar reprojecci?n (lo que sea eso)
813
        }
814

    
815
        /**
816
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByPoint(java.awt.geom.Point2D,
817
         *      double)
818
         */
819
        /*
820
         * public void selectByPoint(Point2D p, double tolerance) throws
821
         * DriverException { Point2D mapPoint = viewPort.toMapPoint((int) p.getX(),
822
         * (int) p.getY()); SelectByPointVisitor visitor = new
823
         * SelectByPointVisitor(); visitor.setQueriedPoint(mapPoint);
824
         * visitor.setTolerance(getViewPort().toMapDistance(3));
825
         *
826
         * try { layers.process(visitor); } catch (VisitException e) { throw new
827
         * RuntimeException("No se espera que SelectByPointVisitor lance esta
828
         * excepci?n", e); } }
829
         */
830

    
831
        /**
832
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByRect(java.awt.geom.Rectangle2D)
833
         */
834
        /*
835
         * public void selectByRect(Rectangle2D rect) throws DriverException {
836
         * FLayer[] actives = layers.getActives(); for (int i=0; i < actives.length;
837
         * i++) { if (actives[i] instanceof FLyrVect) { FLyrVect lyrVect =
838
         * (FLyrVect) actives[i]; FBitSet oldBitSet = lyrVect.getSelection();
839
         * FBitSet newBitSet = lyrVect.queryByRect(rect); newBitSet.xor(oldBitSet);
840
         * lyrVect.setSelection(newBitSet); } }
841
         *  }
842
         */
843

    
844
        /**
845
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
846
         *      int)
847
         */
848
        public void selectByShape(IGeometry g, int relationship) {
849
        }
850

    
851
        /**
852
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByPoint(Point2D,
853
         *      double)
854
         */
855
        public Record[] queryByPoint(Point2D p, double tolerance) {
856
                return null;
857
        }
858

    
859
        /**
860
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByRect(java.awt.geom.Rectangle2D)
861
         */
862
        public Record[] queryByRect(Rectangle2D rect) {
863
                return null;
864
        }
865

    
866
        /**
867
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
868
         *      int)
869
         */
870
        public Record[] queryByShape(IGeometry g, int relationship) {
871
                return null;
872
        }
873

    
874
        /**
875
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#getSelectionBounds()
876
         *
877
         * @see SelectedZoomVisitor#getSelectBound()
878
         */
879
        public Rectangle2D getSelectionBounds() {
880
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
881

    
882
                try {
883
                        layers.process(visitor);
884
                } catch (ReadDriverException e1) {
885
                        throw new RuntimeException(
886
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
887
                                        e1);
888
                } catch (VisitorException e) {
889
                        throw new RuntimeException(
890
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
891
                                        e);
892
                }
893

    
894
                return visitor.getSelectBound();
895
        }
896

    
897
        /**
898
         * <p>Draws this map if its {@link ViewPort ViewPort} has an extent defined:<br>
899
         * <ol>
900
         * <li>Selects only the layers that have to be drawn: {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
901
         * <li>Sets quality: antialiasing by text and images, and quality rendering.
902
         * <li>Draws the layers.
903
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
904
         * <li>Draws the graphic layer.
905
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
906
         * <li>Invokes the garbage collector and memory clean.
907
         * </ol></p>
908
         *
909
         * @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.
910
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
911
         * @param cancel an object thread that implements the {@link Cancellable Cancellable} interface, and will allow to cancel the draw
912
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
913
         * @throws DriverException if fails using some driver.
914
         */
915
        public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
916
                        double scale) throws ReadDriverException {
917
                if (viewPort.getExtent() == null) {
918
                        // System.err.println("viewPort.getExtent() = null");
919
                        return;
920
                }
921
                System.out.println("Viewport despues: " + viewPort.toString());
922
                /*
923
                 * if ((viewPort.getImageWidth() <=0) || (viewPort.getImageHeight() <=
924
                 * 0)) { return; }
925
                 */
926

    
927
                prepareDrawing(image, g, scale);
928

    
929
                // M?s c?lidad al texto
930
                RenderingHints renderHints = new RenderingHints(
931
                                RenderingHints.KEY_ANTIALIASING,
932
                                RenderingHints.VALUE_ANTIALIAS_ON);
933
                renderHints.put(RenderingHints.KEY_RENDERING,
934
                                RenderingHints.VALUE_RENDER_QUALITY);
935
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
936
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
937
                g.setRenderingHints(renderHints);
938

    
939
                long t1 = System.currentTimeMillis();
940
                layers.draw(image, g, viewPort, cancel, scale);
941

    
942
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
943
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
944
                fireLayerDrawingEvent(beforeTracLayerEvent);
945
                tracLayer.draw(image, g, viewPort, cancel, scale);
946
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
947
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
948
                fireLayerDrawingEvent(afterTracLayerEvent);
949

    
950
                //layers.setDirty(false);
951
                long t2 = System.currentTimeMillis();
952
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
953
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
954
                /*
955
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
956
                 * GeneralPath(viewPort.getExtent());
957
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
958
                 */
959
                System.gc();
960
        }
961
        /**
962
         * <p>Checks out layers that need to be repainted.</p>
963
         * <p>If one layer node uses a cache with previous image drawn, but hasn't any, or if it's dirty, then,
964
         *  that layer must be repainted. </p>
965
         * <p>If one layer node needn't to be repainted, checks out it recursively.</p>
966
         * <p><i>The cache of "previous image drawn" allows accelerate the repaint process.</i></p>
967
         *
968
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
969
         * @see #recursivePrepareDrawing(FLayers, int)
970
         */
971
        private void prepareDrawing(BufferedImage image, Graphics2D g, double scale) {
972

    
973
                // Primera pasada: si alguna capa necesita repintarse por debajo
974
                // de la que tiene la cache, TODAS necesitan
975
                // ser repintadas.
976
                boolean bNeedRepaint = false;
977
                boolean bMayExistAceleration = false;
978
                for (int i = 0; i < layers.getLayersCount(); i++)
979
                {
980
                        FLayer lyr = layers.getLayer(i);
981
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
982
                        {
983
                                bMayExistAceleration = true;
984
                        }
985
                        else
986
                        {
987
                                if (lyr.isDirty())
988
                                        bNeedRepaint = true;
989
                        }
990
                }
991
                if (bMayExistAceleration==false)
992
                        bNeedRepaint = true;
993
                if (bNeedRepaint)
994
                        layers.setDirty(true);
995
                else
996
                        recursivePrepareDrawing(layers);
997
        }
998
        /**
999
         * <p>Invoked by {@linkplain MapContext#recursivePrepareDrawing(FLayers, int)}. Sets all previous layer nodes
1000
         *  in the argument as not dirty, what means that don't need to be repainted.</p>
1001
         * <p>Each layer node can decide validate or not it's sub-layers according its implementation.</p>
1002
         * <p>This is useful when it's editing, for accelerate the draw, because the layers of a {@link FLayers FLayers} node
1003
         * are painted according its index in the collection, and each one upper the previous.</p>
1004
         *
1005
         * @param layers a node with layers
1006
         * @param index index of the current layer in the internal list of layers
1007
         *
1008
         * @see #recursivePrepareDrawing(FLayers, int)
1009
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1010
         */
1011
        private void validatePreviousLayers(FLayers layers, int index)
1012
        {
1013
                // TODO: Aqu? quiz?s habr?a que explorar los padres de las capas
1014
                // para marcar y/o asignar la imagen cacheada.
1015
                for (int i = 0; i < index; i++)
1016
                {
1017
                        FLayer lyr = layers.getLayer(i);
1018
                        lyr.setDirty(false);
1019
                }
1020
                // Las de arriba las marcamos como sucias
1021
//                for (int i = index; i < layers.getLayersCount(); i++)
1022
//                {
1023
//                        FLayer lyr = layers.getLayer(i);
1024
//                        lyr.setDirty(true);
1025
//                }
1026
        }
1027
        /**
1028
         * <p>Checks out recursively, layers that have a cache with an image of previous layers drawn, and if are dirty
1029
         * notify the previous layers that are valid.</p>
1030
         *
1031
         * @param parent node that contains the layers
1032
         * @param indexInParent the least layer index in the <code>parent</code> argument. This allows reduce the time processing
1033
         *  if is known that the first layers aren't a collection and will (or won't) be validated
1034
         *
1035
         * @see #prepareDrawing(BufferedImage, Graphics2D, double)
1036
         */
1037
        private void recursivePrepareDrawing(FLayers parent)
1038
        {
1039
                for (int i = 0; i < parent.getLayersCount(); i++)
1040
                {
1041
                        FLayer lyr = parent.getLayer(i);
1042
                        if (lyr.isCachingDrawnLayers() && (lyr.getCacheImageDrawnLayers() != null))
1043
                        {
1044
                                // les decimos a las anteriores que est?n validadas (not dirty)
1045
                                // para que no se dibujen.
1046
                                if (lyr.isDirty())
1047
                                        validatePreviousLayers(parent, i);
1048
                        }
1049

    
1050
                        if (lyr instanceof FLayers)
1051
                        {
1052
                                recursivePrepareDrawing((FLayers)lyr);
1053
                        }
1054
                }
1055

    
1056
        }
1057

    
1058
        /**
1059
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1060
         *
1061
         * @param image image used to accelerate the screen draw
1062
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1063
         * @param cancel an object thread that implements the {@link Cancellable Cancellable} interface, and will allow to cancel the draw
1064
         * @param scale value that represents the scale
1065
         * @throws DriverException if fails using some driver.
1066
         *
1067
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1068
         */
1069
        public void drawGraphics(BufferedImage image, Graphics2D g,
1070
                        Cancellable cancel, double scale) throws ReadDriverException {
1071
                if (viewPort == null)
1072
                        return;
1073
                tracLayer.draw(image, g, viewPort, cancel, scale);
1074
        }
1075

    
1076
        /**
1077
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1078
         *  the task as cancellable.</p>
1079
         *
1080
         * @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.
1081
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1082
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1083
         *
1084
         * @throws DriverException if fails using some driver.
1085
         *
1086
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1087
         */
1088
        public void draw(BufferedImage image, Graphics2D g, double scale)
1089
                        throws ReadDriverException {
1090
                layers.setDirty(true);
1091
                draw(image, g, new Cancellable() {
1092
                        /**
1093
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1094
                         */
1095
                        public boolean isCanceled() {
1096
                                return false;
1097
                        }
1098

    
1099
                        public void setCanceled(boolean canceled) {
1100
                                // TODO Auto-generated method stub
1101

    
1102
                        }
1103
                }, scale);
1104
        }
1105

    
1106
        /**
1107
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1108
         *
1109
         * @return the view port
1110
         *
1111
         * @see #setViewPort(ViewPort)
1112
         */
1113
        public ViewPort getViewPort() {
1114
                return viewPort;
1115
        }
1116

    
1117
        /**
1118
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1119
         *  of this map.</p>
1120
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1121
         *  adds the new one.</p>
1122
         *
1123
         * @param viewPort the viewPort
1124
         *
1125
         * @see #getViewPort()
1126
         */
1127
        public void setViewPort(ViewPort viewPort) {
1128
                if (this.viewPort != null) {
1129
                        this.viewPort.removeViewPortListener(eventBuffer);
1130
                }
1131

    
1132
                this.viewPort = viewPort;
1133
                if (viewPort != null)
1134
                        viewPort.addViewPortListener(eventBuffer);
1135
        }
1136

    
1137
        /**
1138
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1139
         *
1140
         * @param extent the extent of the new zoom
1141
         */
1142
        public void zoomToExtent(Rectangle2D extent) {
1143
                if (extent!=null)
1144
                        getViewPort().setExtent(extent);
1145
        }
1146

    
1147
        /**
1148
         * <p>Returns the union of all extents of all layers of this map.</p>
1149
         *
1150
         * @return full extent of layers of this map
1151
         * @throws DriverException if fails using a driver.
1152
         *
1153
         * @see FLayers#getFullExtent()
1154
         */
1155
        public Rectangle2D getFullExtent() throws ReadDriverException {
1156
                return layers.getFullExtent();
1157
        }
1158

    
1159
        /**
1160
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1161
         * <ul>
1162
         * <li>XML entity of the internal {@link ViewPort ViewPort}.
1163
         * <li>XML entity of the internal {@link FLayers FLayers}.
1164
         * </ul>
1165
         *
1166
         * @return XMLEntity the XML entity
1167
         * @throws XMLException if there is any error creating the XML from the map.
1168
         *
1169
         * @see #createFromXML(XMLEntity)
1170
         * @see #createFromXML03(XMLEntity)
1171
         * @see ViewPort#getXMLEntity()
1172
         * @see FLayers#getXMLEntity()
1173
         */
1174
        public XMLEntity getXMLEntity() throws XMLException {
1175
                XMLEntity xml = new XMLEntity();
1176
                xml.putProperty("className", this.getClass().getName());
1177
                xml.addChild(viewPort.getXMLEntity());
1178
                xml.addChild(layers.getXMLEntity());
1179

    
1180
                return xml;
1181
        }
1182
        /**
1183
         * <p>Creates a new <code>MapContext</code> from an XML entity, with
1184
         *  the data of the {@link ViewPort ViewPort} and
1185
         *  {@link FLayers FLayers}.</p>
1186
         *
1187
         * @param xml an XML entity
1188
         *
1189
         * @return the new <code>MapContext</code>
1190
         *
1191
         * @throws XMLException if there is any error creating the map from the XML.
1192
         *
1193
         * @see #getXMLEntity()
1194
         * @see #createFromXML(XMLEntity)
1195
         * @see ViewPort#createFromXML03(XMLEntity)
1196
         * @see FLayers#setXMLEntity03(XMLEntity)
1197
         */
1198
        public static MapContext createFromXML03(XMLEntity xml) throws XMLException {
1199
                ViewPort vp = ViewPort.createFromXML03(xml.getChild(0));
1200
                MapContext fmap = new MapContext(vp);
1201
                fmap.layers.setXMLEntity03(xml.getChild(1));
1202

    
1203
                return fmap;
1204
        }
1205

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

    
1230
        /**
1231
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1232
         *
1233
         * @param listener the new listener
1234
         *
1235
         * @return <code>true</code> if has added the listener successfully
1236
         *
1237
         * @see #removeAtomicEventListener(AtomicEventListener)
1238
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1239
         */
1240
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1241
                return eventBuffer.addAtomicEventListener(listener);
1242
        }
1243

    
1244
        /**
1245
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1246
         *
1247
         * @param listener the listener to remove
1248
         *
1249
     * @return <tt>true</tt> if the list contained the specified element
1250
         *
1251
         * @see #addAtomicEventListener(AtomicEventListener)
1252
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1253
         */
1254
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1255
                return eventBuffer.removeAtomicEventListener(listener);
1256
        }
1257

    
1258
        /**
1259
         * @see EventBuffer#beginAtomicEvent()
1260
         *
1261
         * @see #endAtomicEvent()
1262
         */
1263
        public void beginAtomicEvent() {
1264
                eventBuffer.beginAtomicEvent();
1265
        }
1266

    
1267
        /**
1268
         * @see EventBuffer#endAtomicEvent()
1269
         *
1270
         * @see #beginAtomicEvent()
1271
         */
1272
        public void endAtomicEvent() {
1273
                eventBuffer.endAtomicEvent();
1274
        }
1275

    
1276
        /**
1277
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1278
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1279
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1280
         *
1281
         * @author Fernando Gonz?lez Cort?s
1282
         */
1283
        public class LayerEventListener implements LayerCollectionListener {
1284
                /**
1285
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1286
                 */
1287
                public void layerAdded(LayerCollectionEvent e) {
1288
                        // Si es la primera capa, fijamos su extent al ViewPort
1289
                        // if (getLayers().getLayersCount() == 1) {
1290
                        if (getViewPort().getExtent() == null) {
1291
                                FLayer lyr = e.getAffectedLayer();
1292
                                if (lyr.isAvailable()) {
1293
                                        try {
1294
                                                getViewPort().setExtent(lyr.getFullExtent());
1295
                                        } catch (ReadDriverException e1) {
1296
                                                e1.printStackTrace();
1297
                                        }
1298
                                }
1299
                        }
1300

    
1301
                        // Registramos al FMap como listener del legend de las capas
1302
                        FLayer lyr = e.getAffectedLayer();
1303
                        selectionListener(lyr);
1304
                }
1305
                /**
1306
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1307
                 *
1308
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1309
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1310
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1311
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1312
                 *
1313
                 * @param the layer or layers
1314
                 */
1315
                private void selectionListener(FLayer lyr){
1316
                        lyr.addLayerListener(eventBuffer);
1317

    
1318
                        if (lyr instanceof Classifiable) {
1319
                                Classifiable c = (Classifiable) lyr;
1320
                                c.addLegendListener(eventBuffer);
1321
                        }
1322

    
1323
                        if (lyr instanceof AlphanumericData) {
1324
                                Selectable s=null;
1325
                                try {
1326
                                        s = ((AlphanumericData) lyr).getRecordset();
1327
                                        if (s!=null) {
1328
                                                s.addSelectionListener(eventBuffer);
1329
                                        }
1330
                                } catch (ReadDriverException e1) {
1331
                                        e1.printStackTrace();
1332
                                }
1333

    
1334
                        }
1335
                        if (lyr instanceof FLayers){
1336
                                FLayers lyrs=(FLayers)lyr;
1337
                                for(int i=0;i<lyrs.getLayersCount();i++){
1338
                                        selectionListener(lyrs.getLayer(i));
1339
                                }
1340
                        }
1341

    
1342
                }
1343
                /*
1344
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1345
                 */
1346
                public void layerMoved(LayerPositionEvent e) {
1347
                }
1348

    
1349
                /*
1350
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1351
                 */
1352
                public void layerRemoved(LayerCollectionEvent e) {
1353
                        FLayer lyr = e.getAffectedLayer();
1354

    
1355
                        lyr.removeLayerListener(eventBuffer);
1356

    
1357
                        if (lyr instanceof Classifiable) {
1358
                                Classifiable c = (Classifiable) lyr;
1359
                                c.removeLegendListener(eventBuffer);
1360
                        }
1361

    
1362
                        if (lyr instanceof Selectable) {
1363
                                Selectable s = (Selectable) lyr;
1364
                                s.addSelectionListener(eventBuffer);
1365
                        }
1366
                }
1367

    
1368
                /*
1369
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1370
                 */
1371
                public void layerAdding(LayerCollectionEvent e)
1372
                                throws CancelationException {
1373
                }
1374

    
1375
                /*
1376
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1377
                 */
1378
                public void layerMoving(LayerPositionEvent e)
1379
                                throws CancelationException {
1380
                }
1381

    
1382
                /*
1383
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1384
                 */
1385
                public void layerRemoving(LayerCollectionEvent e)
1386
                                throws CancelationException {
1387
                }
1388

    
1389

    
1390
                /*
1391
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1392
                 */
1393
                public void visibilityChanged(LayerCollectionEvent e)
1394
                                throws CancelationException {
1395
                }
1396
        }
1397
        /**
1398
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1399
         *  collection of layers argument.</p>
1400
         *
1401
         * @param a collection of layers
1402
         */
1403
        public void addAsCollectionListener(FLayers layers2) {
1404
                layers2.addLayerCollectionListener(layerEventListener);
1405
        }
1406
        /**
1407
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1408
         *
1409
         * @return the graphic layer of this map
1410
         *
1411
         * @see #setGraphicsLayer(GraphicLayer)
1412
         */
1413
        public GraphicLayer getGraphicsLayer() {
1414
                return tracLayer;
1415
        }
1416

    
1417
        /**
1418
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1419
         *
1420
         * @param graphicLayer the new graphic layer
1421
         *
1422
         * @see #getGraphicsLayer()
1423
         */
1424
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1425
                tracLayer = graphicLayer;
1426
        }
1427
        /**
1428
         * <p>Indicates whether some other object is "equal to" this map.</p>
1429
         * <p>Returns <code>true</code> if success one of this options:
1430
         * <ol>
1431
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1432
         * <li>Both maps have the same layers.
1433
         * <li>Both maps have the same number of layers and with the same name.
1434
         * </ol>
1435
         * </p>
1436
         *
1437
         * @param obj the reference object with which to compare.
1438
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument; <code>false</code> otherwise.
1439
         *
1440
         * @see Object#equals(Object)
1441
         */
1442
        public boolean equals(Object arg0) {
1443
                MapContext map = (MapContext) arg0;
1444
                if (super.equals(arg0))
1445
                        return true;
1446
                if (getLayers() == map.getLayers())
1447
                        return true;
1448
                boolean isEqual = true;
1449
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1450
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1451

    
1452
                                if (!getLayers().getLayer(i).getName().equals(
1453
                                                map.getLayers().getLayer(i).getName())) {
1454
                                        isEqual = false;
1455
                                }
1456

    
1457
                        }
1458
                } else {
1459
                        isEqual = false;
1460
                }
1461
                return isEqual;
1462
        }
1463
        /**
1464
         * <p>Registers the message of an error associated to this map.</p>
1465
         *
1466
         * @param stringProperty the error message
1467
         *
1468
         * @see #getLayersError()
1469
         * @see #clearErrors()
1470
         */
1471
        public void addLayerError(String stringProperty) {
1472
                layersError.add(stringProperty);
1473
        }
1474
        /**
1475
         * <p>Gets the list with all errors messages registered to this map.</p>
1476
         *
1477
         * @return the list of errors registered to this map
1478
         *
1479
         * @see #addLayerError(String)
1480
         * @see #clearErrors()
1481
         */
1482
        public ArrayList getLayersError() {
1483
                return layersError;
1484
        }
1485
        /**
1486
         * <p>Removes all error messages associated to this map.</p>
1487
         *
1488
         * @see #addLayerError(String)
1489
         * @see #getLayersError()
1490
         */
1491
        public void clearErrors() {
1492
                layersError.clear();
1493
        }
1494
        /**
1495
         * <p>Removes from this map, all caching images of drawn layers, registered.</p>
1496
         *
1497
         * @see #clearCachingImageDrawnLayers(FLayers)
1498
         */
1499
        public void clearAllCachingImageDrawnLayers() {
1500
                clearCachingImageDrawnLayers(this.layers);
1501
        }
1502
        /**
1503
         * <p>Removes from the layer collection argument, all caching images of drawn layers, registered.</p>
1504
         *
1505
         * @param layers a layer collection
1506
         *
1507
         * @see #clearAllCachingImageDrawnLayers()
1508
         */
1509
        private void clearCachingImageDrawnLayers(FLayers layers){
1510
                int i;
1511
                FLayer layer;
1512
                for (i=0;i< layers.getLayersCount();i++){
1513
                        layer = layers.getLayer(i);
1514
                        if (layer instanceof FLayers) {
1515
                                clearCachingImageDrawnLayers((FLayers)layer);
1516
                        } else {
1517
                                layer.setCacheImageDrawnLayers(null);
1518
                        }
1519
                }
1520
        }
1521

    
1522

    
1523

    
1524
        public FLayers getNewGroupLayer(FLayers parent) {
1525
            return new FLayers(this, parent);
1526
        }
1527
}