Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / MapContext.java @ 38058

History | View | Annotate | Download (53.8 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.lang.reflect.InvocationTargetException;
52
import java.util.ArrayList;
53
import java.util.List;
54
import java.util.prefs.Preferences;
55

    
56
import javax.print.attribute.PrintRequestAttributeSet;
57

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

    
62
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
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.layers.name.LayerNameUtils;
80
import com.iver.cit.gvsig.fmap.layers.order.DefaultOrderManager;
81
import com.iver.cit.gvsig.fmap.layers.order.OrderManager;
82
import com.iver.cit.gvsig.fmap.operations.Cancel;
83
import com.iver.cit.gvsig.fmap.operations.selection.Record;
84
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
85
import com.iver.cit.gvsig.fmap.operations.strategies.SelectedZoomVisitor;
86
import com.iver.cit.gvsig.fmap.rendering.LegendListener;
87
import com.iver.utiles.XMLEntity;
88
import com.iver.utiles.swing.threads.Cancellable;
89

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

    
148

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

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

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

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

    
178

    
179
    }
180

    
181

    
182

    
183
        public static void addAreaUnit(String name, String abbr,boolean isLinear,double trans2meter){
184
                if (!AREANAMES.contains(name)){
185
                        AREANAMES.add(name);
186
                        String pow="";
187
                        if (isLinear)
188
                                pow=String.valueOf((char)178);
189
                        AREAABBR.add(abbr+pow);
190
                        AREATRANS2METER.add(new Double(trans2meter));
191
                }
192
        }
193
        public static String[] getAreaNames(){
194
                return (String[])AREANAMES.toArray(new String[0]);
195
        }
196
        public static String[] getAreaAbbr(){
197
                return (String[])AREAABBR.toArray(new String[0]);
198
        }
199
        public static double[] getAreaTrans2Meter(){
200
                int size=AREATRANS2METER.size();
201
                double[] trans2meters=new double[size];
202
                for (int i = 0; i < size; i++) {
203
                        trans2meters[i]=((Double)AREATRANS2METER.get(i)).doubleValue();
204
                }
205
                return trans2meters;
206
        }
207
        public static String getOfLinear(int i) {
208
                if (((String)AREAABBR.get(i)).toLowerCase().endsWith(String.valueOf((char)178))){
209
                        return String.valueOf((char)178);
210
                }
211
                return "";
212
        }
213
        public static void addDistanceUnit(String name, String abbr,double trans2meter){
214
                if (!DISTANCENAMES.contains(name)){
215
                        DISTANCENAMES.add(name);
216
                        DISTANCEABBR.add(abbr);
217
                        DISTANCETRANS2METER.add(new Double(trans2meter));
218
                }
219
        }
220
        public static String[] getDistanceNames(){
221
                return (String[])DISTANCENAMES.toArray(new String[0]);
222
        }
223
        public static String[] getDistanceAbbr(){
224
                return (String[])DISTANCEABBR.toArray(new String[0]);
225
        }
226
        public static double[] getDistanceTrans2Meter(){
227
                int size=DISTANCETRANS2METER.size();
228
                double[] trans2meters=new double[size];
229
                for (int i = 0; i < size; i++) {
230
                        trans2meters[i]=((Double)DISTANCETRANS2METER.get(i)).doubleValue();
231
                }
232
                return trans2meters;
233
        }
234
        public static int getDistancePosition(String s){
235
                for (int i = 0; i < DISTANCENAMES.size(); i++) {
236
                        if (DISTANCENAMES.get(i).equals(s)){
237
                                return i;
238
                        }
239
                }
240
                return 0;
241
        }
242

    
243
        /**
244
         * <p>Defines the value which a unit of a distance measurement must be divided to obtain its equivalent <b>in centimeters</b>.</p>
245
         *
246
         * <p><b><i>Conversion values of distance measurements:</i></b>
247
         * <ul>
248
         *  <li><code>MapContext.CHANGE[0]</code>: kilometer
249
         *  <li><code>MapContext.CHANGE[1]</code>: meter
250
         *  <li><code>MapContext.CHANGE[2]</code>: centimeter
251
         *  <li><code>MapContext.CHANGE[3]</code>: millimeter
252
         *  <li><code>MapContext.CHANGE[4]</code>: international statute mile
253
         *  <li><code>MapContext.CHANGE[5]</code>: yard
254
         *  <li><code>MapContext.CHANGE[6]</code>: foot
255
         *  <li><code>MapContext.CHANGE[7]</code>: inch
256
         *  <li><code>MapContext.CHANGE[8]</code>: grade
257
         * </ul>
258
         *
259
         * <p><h3>Examples:</h3>
260
         * <pre>1 international statute mile / MapContext.CHANGE[4] = X centimeters</pre>
261
         * <pre>1 kilometer / MapContext.CHANGE[0] = X centimeters</pre>
262
         * <pre>1 grade / MapContext.CHANGE[8] = X centimeters</pre>
263
         * </p>
264
         *
265
         * <p><h3>Grade conversion value: <code>MapContext.CHANGE[8]</code></h3>
266
         * The value of <code>MapContext.CHANGE[8]</code> represents the centimeters of a straight line between two
267
         *  points on the Earth surface that are 1 grade far each other of the center of the Earth. This value has been calculated using
268
         *  a radius approximated of R<sub>Earth</sub>=6.37846082678100774672e6 meters, according these equations:
269
         * <pre>D = 2 * (sin (1)) * R<sub>Earth</sub></pre>
270
         * <pre>MapContext.CHANGE[8] = 1 / D</pre>
271
         * <h4>Explanation:</h4>
272
         * We get an isosceles triangle with the center of the Earth and the 2 points on the surface. This triangle can be divided into
273
         * 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
274
         * is the hypotenuse. Then we apply trigonometry and get the distance <i>D</i> between both points on the Earth surface.</p>
275
         * <p>Now we only must invert that value to obtain <code>MapContext.CHANGE[8]</code>.</p>
276
         * @deprecated use getDistanceTrans2Meter() * 100
277
         */
278
        public static final double[] CHANGE = { 100000, 100, 1, 0.1, 160934.4,
279
                        91.44, 30.48, 2.54, 1/8.983152841195214E-4 };
280

    
281
        /* Do not alter the order and the values of this array, if you need append values.*/
282
        /**
283
         * <p>Gets the name of all distance measurements supported by <code>MapContext</code>.</p>
284
         */
285
//        public static final String[] NAMES= {
286
//                Messages.getString("Kilometros"),
287
//                Messages.getString("Metros"),
288
//                Messages.getString("Centimetros"),
289
//                Messages.getString("Milimetros"),
290
//                Messages.getString("Millas"),
291
//                Messages.getString("Yardas"),
292
//                Messages.getString("Pies"),
293
//                Messages.getString("Pulgadas"),
294
//                Messages.getString("Grados"),
295
//        };
296

    
297
        public static final int EQUALS = 0;
298

    
299
        public static final int DISJOINT = 1;
300

    
301
        public static final int INTERSECTS = 2;
302

    
303
        public static final int TOUCHES = 3;
304

    
305
        public static final int CROSSES = 4;
306

    
307
        public static final int WITHIN = 5;
308

    
309
        public static final int CONTAINS = 6;
310

    
311
        public static final int OVERLAPS = 7;
312

    
313
        /**
314
         * A hierarchy of {@link FLayers FLayers} nodes.
315
         *
316
         * @see #getLayers()
317
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
318
         */
319
        protected FLayers layers;
320

    
321
        /**
322
         * A layer with graphical items: geometries and symbols.
323
         *
324
         * @see #getGraphicsLayer()
325
         * @see #setGraphicsLayer(GraphicLayer)
326
         * @see #drawGraphics(BufferedImage, Graphics2D, Cancellable, double)
327
         * @see #print(Graphics2D, double, PrintRequestAttributeSet)
328
         */
329
        private GraphicLayer tracLayer = new GraphicLayer();
330

    
331
        /**
332
         * Information for draw layers in a view.
333
         *
334
         * @see #getViewPort()
335
         * @see #setViewPort(ViewPort)
336
         */
337
        private ViewPort viewPort;
338

    
339
        // private ArrayList invalidationListeners = new ArrayList();
340

    
341
        /**
342
         * Array list with all {@link LegendListener LegendListener} registered to this map.
343
         *
344
         * @see #addLayerListener(LegendListener)
345
         * @see #removeLayerListener(LegendListener)
346
         * @see #callLegendChanged()
347
         */
348
        private ArrayList legendListeners = new ArrayList();
349

    
350
        /**
351
         * Array list with all {@link LayerDrawingListener LayerDrawingListener} registered to this map.
352
         *
353
         * @see #addLayerDrawingListener(LayerDrawingListener)
354
         * @see #removeLayerDrawListener(LayerDrawingListener)
355
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
356
         */
357
        private ArrayList layerDrawingListeners = new ArrayList();
358

    
359
        /**
360
         * <p>Buffer that is used to store and eject events produced on this map:
361
         * <ul>
362
         *  <li>Layer collection events.
363
         *  <li>View port events.
364
         *  <li>Atomic events.
365
         *  <li>Layer events.
366
         *  <li>Legend events on a {@link Classificable Classificable} layer.
367
         *  <li>Selection events on an {@link AlphanumericData AlphanumericData} data layer.
368
         * </ul>
369
         * </p>
370
         *
371
         * @see #addAtomicEventListener(AtomicEventListener)
372
         * @see #removeAtomicEventListener(AtomicEventListener)
373
         * @see #beginAtomicEvent()
374
         * @see #endAtomicEvent()
375
         */
376
        private EventBuffer eventBuffer = new EventBuffer();
377

    
378
        /**
379
         * Event listener for the collection of layers of this map.
380
         */
381
        private LayerEventListener layerEventListener = null;
382

    
383
        /**
384
         * List with information of all errors produced on all layers.
385
         *
386
         * @see #addLayerError(String)
387
         * @see #getLayersError()
388
         * @see #clearErrors()
389
         */
390
        private ArrayList layersError = new ArrayList();
391

    
392
        /**
393
         * Array list with all {@link ErrorListener ErrorListener} registered to this map.
394
         *
395
         * @see #addErrorListener(ErrorListener)
396
         * @see #removeErrorListener(LegendListener)
397
         * @see #callNewErrorEvent(ErrorEvent)
398
         * @see #reportDriverExceptions(String, List)
399
         */
400
        private ArrayList errorListeners = new ArrayList();
401

    
402

    
403

    
404
        // public static ResourceBundle myResourceBundle =
405
        // ResourceBundle.getBundle("FMap");
406

    
407
        /**
408
         * <p>Default <i>zoom in</i> factor.</p>
409
         * <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
410
         * area but with the items bigger.</p>
411
         */
412
        public static double ZOOMINFACTOR=2;
413

    
414
        /**
415
         * <p>Default <i>zoom out</i> factor.</p>
416
         * <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
417
         * area but with the items smaller.</p>
418
         */
419
        public static double ZOOMOUTFACTOR=0.5;
420

    
421
        /**
422
         *          * Draw version of the context. It's used for know when de componend has
423
         * changed any visualization property
424
         *
425
         *  @see getDrawVersion
426
         *  @see updateDrawVersion
427
         */
428
        private long drawVersion= 0L;
429

    
430
        /**
431
         * Object to Manage Draw of MapContext
432
         */
433
        private MapContextDrawer mapContextDrawer= null;
434

    
435
        /**
436
         * Object to Manage Draw of MapContext
437
         */
438
        private Class mapContextDrawerClass = DefaultMapContextDrawer.class;
439
        private Class defaultMapContextDrawerClass = DefaultMapContextDrawer.class;
440

    
441
        /**
442
         * <p>Color used to represent the selections.</p>
443
         */
444
        private static Color selectionColor = Color.YELLOW;
445

    
446

    
447
        /**
448
         * <p>Gets the color used to represent the selections.</p>
449
         *
450
         * @return color used to represent the selections
451
         */
452
        public static Color getSelectionColor() {
453
                return selectionColor;
454
        }
455

    
456
        /**
457
         * <p>Sets the color used to represent the selections.</p>
458
         *
459
         * @param selectionColor color used to represent the selections
460
         */
461
        public static void setSelectionColor(Color selectionColor) {
462
                MapContext.selectionColor = selectionColor;
463
        }
464

    
465
        /**
466
         * <p>Creates a new map context with the drawing information defined in the view port argument, and
467
         *  without layers.</p>
468
         *
469
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
470
         */
471
        public MapContext(ViewPort vp) {
472
                this.layers = new FLayers();//(this,null);
473
                this.layers.setMapContext(this);
474

    
475
                layerEventListener = new LayerEventListener();
476
                layers.addLayerCollectionListener(layerEventListener);
477
                layers.addLayerCollectionListener(eventBuffer);
478

    
479
                setViewPort(vp);
480

    
481
        }
482

    
483
        /**
484
         * <p>Creates a new map context with the layers and the drawing information defined in the view port arguments.</p>
485
         *
486
         * @param fLayers the initial hierarchy of nodes of layers that this map will have
487
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
488
         */
489
        public MapContext(FLayers fLayers, ViewPort vp) {
490
                this.layers = fLayers;
491

    
492
                layerEventListener = new LayerEventListener();
493
                layers.addLayerCollectionListener(layerEventListener);
494
                layers.addLayerCollectionListener(eventBuffer);
495

    
496
                setViewPort(vp);
497
        }
498

    
499
        /**
500
         * <p>Reports to all driver error listeners registered of a bundle of driver exceptions caused in the same map atomic transaction.</p>
501
         *
502
         * @param introductoryText introductory text specified by developer. If <code>null</code>, use ""
503
         * @param driverExceptions list with a bundle of driver exceptions caught during an atomic event
504
         *
505
         * @see #addErrorListener(ErrorListener)
506
         * @see #removeErrorListener(LegendListener)
507
         * @see #callNewErrorEvent(ErrorEvent)
508
         */
509
        public synchronized void reportDriverExceptions(String introductoryText,
510
                                                                                                        List driverExceptions){
511
                for (int i = 0; i < errorListeners.size(); i++) {
512
                        ((ErrorListener) errorListeners.get(i)).
513
                                reportDriverExceptions(introductoryText, driverExceptions);
514
                }
515
        }
516

    
517
        /**
518
         * <p>Adds the specified legend listener (if didn't exist) to receive legend events from this map.</p>
519
         *
520
         * @param listener the legend listener
521
         *
522
         * @see #removeLayerListener(LegendListener)
523
         * @see #callLegendChanged()
524
         */
525
        public void addLayerListener(LegendListener listener) {
526
                if (!legendListeners.contains(listener))
527
                        legendListeners.add(listener);
528
        }
529
        // SUGERENCIA DE PABLO
530
        //        public void addLegendListener(LegendListener listener) {
531
        //                if (!legendListeners.contains(listener))
532
        //                        legendListeners.add(listener);
533
        //        }
534

    
535
        /**
536
         * <p>Adds the specified layer drawing listener to catch and handle drawing events from layers of this map.</p>
537
         *
538
         * @param listener the listener to add
539
         *
540
         * @see #removeLayerDrawListener(LayerDrawingListener)
541
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
542
         */
543
        public void addLayerDrawingListener(LayerDrawingListener listener) {
544
                layerDrawingListeners.add(listener);
545
        }
546

    
547
        /**
548
         * <p>Removes the specified layer drawing listener from this map.</p>
549
         *
550
         * @param listener the listener to remove
551
         *
552
         * @see #addLayerDrawingListener(LayerDrawingListener)
553
         * @see #fireLayerDrawingEvent(LayerDrawEvent)
554
         */
555
        public void removeLayerDrawListener(LayerDrawingListener listener) {
556
                layerDrawingListeners.remove(listener);
557
        }
558

    
559
        /**
560
         * <p>Adds the specified error listener to receive error events from this map.</p>
561
         *
562
         * @param listener the listener to add
563
         *
564
         * @see #removeErrorListener(LegendListener)
565
         * @see #callNewErrorEvent(ErrorEvent)
566
         * @see #reportDriverExceptions(String, List)
567
         */
568
        public void addErrorListener(ErrorListener listener) {
569
                errorListeners.add(listener);
570
        }
571

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

    
585
        // SUGERENCIA DE PABLO:
586
        //public void removeErrorListener(ErrorListener listener) {
587
        //        errorListeners.remove(listener);
588
        //}
589

    
590
        /**
591
         * <p>Notifies to all legend listeners registered, that one legend has changed.</p>
592
         * <p>This method must be called only if it's wanted to reflect a legend change.</p>
593
         *
594
         * @see #addLayerListener(LegendListener)
595
         * @see #removeLayerListener(LegendListener)
596
         */
597
        public synchronized void callLegendChanged() {
598
                for (int i = 0; i < legendListeners.size(); i++) {
599
                        ((LegendListener) legendListeners.get(i)).legendChanged(null);
600
                }
601
                // getLayers().moveTo(0,0);
602
        }
603

    
604
        /**
605
         * <p>Fires a layer drawing event to all {@link LayerDrawingListener LayerDrawingListener} listeners registered,
606
         *  distinguishing the kind of event.</p>
607
         *
608
         * @param e the event
609
         *
610
         * @see #addLayerDrawingListener(LayerDrawingListener)
611
         * @see #removeLayerDrawListener(LayerDrawingListener)
612
         */
613
        public synchronized void fireLayerDrawingEvent(LayerDrawEvent e) {
614
                for (int i = 0; i < layerDrawingListeners.size(); i++)
615
                {
616
                        LayerDrawingListener listener = (LayerDrawingListener) layerDrawingListeners.get(i);
617
                        switch (e.getEventType())
618
                        {
619
                                case LayerDrawEvent.LAYER_BEFORE_DRAW:
620
                                        listener.beforeLayerDraw(e);
621
                                        break;
622
                                case LayerDrawEvent.LAYER_AFTER_DRAW:
623
                                        listener.afterLayerDraw(e);
624
                                        break;
625
                                case LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW:
626
                                        listener.beforeGraphicLayerDraw(e);
627
                                        break;
628
                                case LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW:
629
                                        listener.afterLayerGraphicDraw(e);
630
                                        break;
631
                        }
632
                }
633
                // getLayers().moveTo(0,0);
634
        }
635

    
636
        /**
637
         * <p>Notifies to all error listeners registered, that one error has been produced.</p>
638
         *
639
         * @param e the event with information of the error
640
         *
641
         * @see #addErrorListener(ErrorListener)
642
         * @see #removeErrorListener(LegendListener)
643
         * @see #reportDriverExceptions(String, List)
644
         */
645
        public synchronized void callNewErrorEvent(ErrorEvent e) {
646
                for (int i = 0; i < errorListeners.size(); i++) {
647
                        ((ErrorListener) errorListeners.get(i)).errorThrown(e);
648
                }
649
                errorListeners.clear();
650
                // getLayers().moveTo(0,0);
651
        }
652

    
653
        /**
654
         * <p>Removes the specified layer listener from this map.</p>
655
         *
656
         * @param listener the listener to remove
657
         *
658
         * @see #addLayerListener(LegendListener)
659
         * @see #callLegendChanged()
660
         */
661
        public void removeLayerListener(LegendListener listener) {
662
                legendListeners.remove(listener);
663
        }
664

    
665
        // SUGERENCIA DE PABLO:
666
        // public void removeLegendListener(LegendListener listener) {
667
        //         legendListeners.remove(listener);
668
        // }
669

    
670
        /**
671
         * <p>Returns the hierarchy of {@link FLayers FLayers} nodes stored in this map.</p>
672
         *
673
         * @return the hierarchy of nodes of layers stored in this map
674
         */
675
        public FLayers getLayers() {
676
                return layers;
677
        }
678

    
679
        /**
680
         * <p>Draws the visible layers of this map according its view port, on the image parameter.</p>
681
         *
682
         * @param b image with an accessible buffer of image data
683
         */
684
        public void drawLabels(BufferedImage b) {
685
        }
686

    
687
        /**
688
         * @see #redraw()
689
         */
690
        public void invalidate() {
691
                if (getLayers().getLayersCount() > 0)
692
                        getLayers().moveTo(0, 0);
693
        }
694

    
695
        /**
696
         * <p>Prints the layers of this map using the {@link Graphics2D Graphics2D} argument, that usually is
697
         * the {@link Graphics Graphics} of the printer.</p>
698
         *
699
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
700
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
701
         * @param properties a set with the settings to be applied to a whole print job and to all the documents in the print job
702
         *
703
         * @throws ReadDriverException if fails reading with driver.
704
         *
705
         * @see FLayers#print(Graphics2D, ViewPort, Cancellable, double, PrintRequestAttributeSet)
706
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
707
         */
708
        public void print(Graphics2D g, double scale, PrintRequestAttributeSet properties) throws ReadDriverException {
709
                RenderingHints renderHints = new RenderingHints(
710
                                RenderingHints.KEY_ANTIALIASING,
711
                                RenderingHints.VALUE_ANTIALIAS_ON);
712
                renderHints.put(RenderingHints.KEY_RENDERING,
713
                                RenderingHints.VALUE_RENDER_QUALITY);
714
                g.setRenderingHints(renderHints);
715

    
716
                Cancel cancel = new Cancel();
717
                this.getMapContextDrawer().print(this.layers, g, cancel, scale,properties);
718
                tracLayer.draw(null, g, viewPort, cancel, scale);
719
        }
720

    
721
        /**
722
         * <p>Returns a new <code>MapContext</code> instance with the information of the <code>vp</code> argument, and the layers of this map.</p>
723
         *
724
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
725
         *
726
         * @return a new <code>MapContext</code> instance projected by <code>vp</code>
727
         */
728
        public MapContext createNewFMap(ViewPort vp) {
729
                MapContext ret = new MapContext(vp);
730
                ret.layers = this.layers;
731

    
732
                return ret;
733
        }
734

    
735
        /**
736
         * <p>Creates a new independent <code>MapContext</code> instance, that has a clone of the layers and the view port of this one.</p>
737
         * <p>The new map will have the same data source drivers to avoid waste memory, and work faster.</p>
738
         *
739
         * @return the new <code>MapContext</code> instance
740
         *
741
         * @throws XMLException if fails cloning the view port or a layer
742
         *
743
         * @see FLayer#cloneLayer()
744
         * @see ViewPort#cloneViewPort()
745
         */
746
        public MapContext cloneFMap() throws XMLException {
747
                ViewPort vp = getViewPort().cloneViewPort();
748
                FLayers antLayers = getLayers();
749
                MapContext ret = new MapContext(vp);
750
                FLayers aux = new FLayers();//(ret,null);
751
                aux.setMapContext(ret);
752
                for (int i=0; i < antLayers.getLayersCount(); i++)
753
                {
754
                        FLayer lyr = antLayers.getLayer(i);
755
                        try {
756
                                aux.addLayer(lyr.cloneLayer());
757
                        } catch (Exception e) {
758
                                e.printStackTrace();
759
//                                throw new XMLException(e);
760
                        }
761
                }
762
                ret.layers = aux;
763
        // FJP: Nuevo para que se vean los graficos                
764
                ret.setGraphicsLayer(this.getGraphicsLayer());
765
        // FJP: Fin nuevo
766

    
767
                
768
                return ret;
769

    
770
//                return createFromXML(getXMLEntity());
771

    
772
        }
773

    
774
        /**
775
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
776
         *
777
         * @return the new map
778
         */
779
        public MapContext cloneToDraw() {
780
                ViewPort vp = getViewPort().cloneViewPort();
781
                MapContext mapContext=new MapContext(getLayers(),vp);
782
                return mapContext;
783
        }
784

    
785
        /**
786
         * A?ade la capa que se pasa como par?metro al nodo que se pasa como
787
         * parametro y lanza ProjectionMismatchException si no est?n todas las capas
788
         * de este FMap en la misma proyecci?n. Lanza un ChildNotAllowedException si
789
         * la capa no es un FLayers y no permite hijos
790
         *
791
         * @param vectorial
792
         *            DOCUMENT ME!
793
         */
794

    
795
        /*
796
         * public void addLayer(LayerPath parent, FLayer layer) throws
797
         * ProjectionMismatchException, ChildrenNotAllowedException {
798
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
799
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
800
         */
801

    
802
        /**
803
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
804
         *
805
         * @param vectorial the layer to add
806
         */
807
        public void addToTrackLayer(FLayer vectorial) {
808
        }
809

    
810
        /**
811
         * <p>Returns the scale of the view in the screen.</p>
812
         *
813
         * @return one of this values:
814
         * <ul>
815
         * <li>the scale of the adjusted extent scale of the view in the screen
816
         * <li><code>-1</code> if there is no image
817
         * <li><code>0</code> if there is no extent defined for the image
818
         * </ul>
819
         *
820
         * @see #setScaleView(long)
821
         * @see ViewPort#getAdjustedExtent()
822
         * @see IProjection#getScale(double, double, double, double)
823
         */
824
        public long getScaleView() {
825
                double dpi = getScreenDPI();
826
                IProjection proj = viewPort.getProjection();
827

    
828
                if (viewPort.getImageSize() == null)
829
                        return -1;
830

    
831
                if (viewPort.getAdjustedExtent() == null) {
832
                        return 0;
833
                }
834
                double[] trans2Meter=getDistanceTrans2Meter();
835
                if (proj == null) {
836
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
837
                        return (long) (viewPort.getAdjustedExtent().getWidth() / w * trans2Meter[getViewPort()
838
                                        .getMapUnits()]);
839
                }
840

    
841
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*trans2Meter[getViewPort().getMapUnits()]),
842
                                (viewPort.getAdjustedExtent().getMaxX()*trans2Meter[getViewPort().getMapUnits()]), viewPort.getImageSize()
843
                                                .getWidth(), dpi));
844

    
845

    
846
        }
847

    
848
        /**
849
         * <p>Sets the new extent of the view, calculated using the scale argument.</p>
850
         * <p>Doesn't updates the scale if there isn't information about the dimension of the image or the
851
         *  adjusted extent.</p>
852
         *
853
         * @param scale the new scale for the view
854
         *
855
         * @see ViewPort#setProjection(IProjection)
856
         * @see #getScaleView()
857
         */
858
        public void setScaleView(long scale) {
859
                //clearAllCachingImageDrawnLayers();
860
                double dpi = getScreenDPI();
861
                if (viewPort.getImageSize() == null)
862
                        return;
863
                IProjection proj = viewPort.getProjection();
864
                if (viewPort.getAdjustedExtent() == null) {
865
                        return;
866
                }
867
                double[] trans2Meter=getDistanceTrans2Meter();
868
                Rectangle2D rec=proj.getExtent(viewPort.getAdjustedExtent(), //extent
869
                                scale, //scale
870
                                viewPort.getImageWidth(), //wImage
871
                                viewPort.getImageHeight(), //hImage
872
                                100*trans2Meter[getViewPort().getMapUnits()], //mapUnits
873
                                trans2Meter[getViewPort().getDistanceUnits()], //distanceUnits
874
                                dpi); //dpi
875
                getViewPort().setExtent(rec);
876
        }
877

    
878
        /**
879
         * <p>Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
880
         * by default as it is defined in the default Toolkit.</p>
881
         *
882
         * @return double with the screen's dpi
883
         */
884
        public static double getScreenDPI() {
885
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
886
                Toolkit kit = Toolkit.getDefaultToolkit();
887
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
888
                return dpi;
889
        }
890

    
891
        /**
892
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
893
         */
894
        public void setVectorial(VectorialAdapter v) {
895
        }
896

    
897
        /**
898
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
899
         */
900
        public void process(FeatureVisitor visitor) {
901
        }
902

    
903
        /**
904
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
905
         */
906
        public void processSelected(FeatureVisitor visitor) {
907
        }
908

    
909
        /**
910
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
911
         *      VectorialSubSet)
912
         */
913
        public void select(FeatureVisitor visitor) {
914
        }
915

    
916
        /**
917
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectFromSelection()
918
         */
919
        public void selectFromSelection() {
920
        }
921

    
922
        /**
923
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#createIndex()
924
         */
925
        public void createIndex() {
926
        }
927

    
928
        /**
929
         * @see org.cresques.geo.Projected#getProjection()
930
         *
931
         * @see ViewPort#getProjection()
932
         * @see #setProjection(IProjection)
933
         * @see #reProject(ICoordTrans)
934
         */
935
        public IProjection getProjection() {
936
                return getViewPort().getProjection();
937
        }
938

    
939
        /**
940
         * <p>Sets the new projection.</p>
941
         *
942
         * @param proj the new projection
943
         *
944
         * @see #getProjection()
945
         * @see ViewPort#setProjection(IProjection)
946
         * @see #reProject(ICoordTrans)
947
         */
948
        public void setProjection(IProjection proj) {
949
                if (getViewPort() != null) {
950
                        getViewPort().setProjection(proj);
951
                }
952
        }
953

    
954
        /**
955
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
956
         */
957
        public void reProject(ICoordTrans arg0) {
958
                // TODO implementar reprojecci?n (lo que sea eso)
959
        }
960

    
961
        /**
962
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByPoint(java.awt.geom.Point2D,
963
         *      double)
964
         */
965
        /*
966
         * public void selectByPoint(Point2D p, double tolerance) throws
967
         * DriverException { Point2D mapPoint = viewPort.toMapPoint((int) p.getX(),
968
         * (int) p.getY()); SelectByPointVisitor visitor = new
969
         * SelectByPointVisitor(); visitor.setQueriedPoint(mapPoint);
970
         * visitor.setTolerance(getViewPort().toMapDistance(3));
971
         *
972
         * try { layers.process(visitor); } catch (VisitException e) { throw new
973
         * RuntimeException("No se espera que SelectByPointVisitor lance esta
974
         * excepci?n", e); } }
975
         */
976

    
977
        /**
978
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByRect(java.awt.geom.Rectangle2D)
979
         */
980
        /*
981
         * public void selectByRect(Rectangle2D rect) throws DriverException {
982
         * FLayer[] actives = layers.getActives(); for (int i=0; i < actives.length;
983
         * i++) { if (actives[i] instanceof FLyrVect) { FLyrVect lyrVect =
984
         * (FLyrVect) actives[i]; FBitSet oldBitSet = lyrVect.getSelection();
985
         * FBitSet newBitSet = lyrVect.queryByRect(rect); newBitSet.xor(oldBitSet);
986
         * lyrVect.setSelection(newBitSet); } }
987
         *  }
988
         */
989

    
990
        /**
991
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
992
         *      int)
993
         */
994
        public void selectByShape(IGeometry g, int relationship) {
995
        }
996

    
997
        /**
998
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByPoint(Point2D,
999
         *      double)
1000
         */
1001
        public Record[] queryByPoint(Point2D p, double tolerance) {
1002
                return null;
1003
        }
1004

    
1005
        /**
1006
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByRect(java.awt.geom.Rectangle2D)
1007
         */
1008
        public Record[] queryByRect(Rectangle2D rect) {
1009
                return null;
1010
        }
1011

    
1012
        /**
1013
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
1014
         *      int)
1015
         */
1016
        public Record[] queryByShape(IGeometry g, int relationship) {
1017
                return null;
1018
        }
1019

    
1020
        /**
1021
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#getSelectionBounds()
1022
         *
1023
         * @see SelectedZoomVisitor#getSelectBound()
1024
         */
1025
        public Rectangle2D getSelectionBounds() {
1026
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
1027

    
1028
                try {
1029
                        layers.process(visitor);
1030
                } catch (ReadDriverException e1) {
1031
                        throw new RuntimeException(
1032
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
1033
                                        e1);
1034
                } catch (VisitorException e) {
1035
                        throw new RuntimeException(
1036
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
1037
                                        e);
1038
                }
1039

    
1040
                return visitor.getSelectBound();
1041
        }
1042

    
1043
        /**
1044
         * <p>Draws this map if its {@link ViewPort ViewPort} has an extent defined:<br>
1045
         * <ol>
1046
         * <li>Selects only the layers that have to be drawn: {@linkplain #prepareDrawing(BufferedImage, Graphics2D, double)}.
1047
         * <li>Sets quality: antialiasing by text and images, and quality rendering.
1048
         * <li>Draws the layers.
1049
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW</code>.
1050
         * <li>Draws the graphic layer.
1051
         * <li>Fires a <code>LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW</code>.
1052
         * <li>Invokes the garbage collector and memory clean.
1053
         * </ol></p>
1054
         *
1055
         * @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.
1056
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1057
         * @param cancel shared object that determines if this layer can continue being drawn
1058
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1059
         * @throws ReadDriverException if fails reading with the driver.
1060
         */
1061
        public void draw(BufferedImage image, Graphics2D g, Cancellable cancel,
1062
                        double scale) throws ReadDriverException {
1063
                if (viewPort.getExtent() == null) {
1064
                        // System.err.println("viewPort.getExtent() = null");
1065
                        return;
1066
                }
1067
                System.out.println("Viewport despues: " + viewPort.toString());
1068
                /*
1069
                 * if ((viewPort.getImageWidth() <=0) || (viewPort.getImageHeight() <=
1070
                 * 0)) { return; }
1071
                 */
1072

    
1073
//                prepareDrawing(image, g, scale);
1074

    
1075
                // M?s c?lidad al texto
1076
                RenderingHints renderHints = new RenderingHints(
1077
                                RenderingHints.KEY_ANTIALIASING,
1078
                                RenderingHints.VALUE_ANTIALIAS_ON);
1079
                renderHints.put(RenderingHints.KEY_RENDERING,
1080
                                RenderingHints.VALUE_RENDER_QUALITY);
1081
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
1082
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1083
                g.setRenderingHints(renderHints);
1084

    
1085
                long t1 = System.currentTimeMillis();
1086
//                layers.draw(image, g, viewPort, cancel, scale);
1087

    
1088
                this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1089

    
1090
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
1091
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1092
                fireLayerDrawingEvent(beforeTracLayerEvent);
1093
                tracLayer.draw(image, g, viewPort, cancel, scale);
1094
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
1095
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1096
                fireLayerDrawingEvent(afterTracLayerEvent);
1097

    
1098
                //layers.setDirty(false);
1099
                long t2 = System.currentTimeMillis();
1100
                System.err.println("Tiempo de dibujado:" + (t2 - t1) +
1101
                                " mseg. Memoria libre:" + Runtime.getRuntime().freeMemory() / 1024  + " KB");
1102
                /*
1103
                 * g.setColor(Color.BLUE); GeneralPath shpR = new
1104
                 * GeneralPath(viewPort.getExtent());
1105
                 * shpR.transform(viewPort.getAffineTransform()); g.draw(shpR);
1106
                 */
1107
                System.gc();
1108
        }
1109
        
1110
        /**
1111
         * <p>Draws only the internal graphic layer using the information of the {@link ViewPort ViewPort} of this map.</p>
1112
         *
1113
         * @param image image used to accelerate the screen draw
1114
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1115
         * @param cancel shared object that determines if this layer can continue being drawn
1116
         * @param scale value that represents the scale
1117
         * @throws ReadDriverException if fails reading with the driver.
1118
         *
1119
         * @see GraphicLayer#draw(BufferedImage, Graphics2D, ViewPort, Cancellable, double)
1120
         */
1121
        public void drawGraphics(BufferedImage image, Graphics2D g,
1122
                        Cancellable cancel, double scale) throws ReadDriverException {
1123
                if (viewPort == null)
1124
                        return;
1125
                tracLayer.draw(image, g, viewPort, cancel, scale);
1126
        }
1127

    
1128
        /**
1129
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1130
         *  the task as cancellable.</p>
1131
         *
1132
         * @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.
1133
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1134
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1135
         *
1136
         * @throws ReadDriverException if the driver fails reading.
1137
         *
1138
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1139
         */
1140
        public void draw(BufferedImage image, Graphics2D g, double scale)
1141
                        throws ReadDriverException {
1142
//                layers.setDirty(true);
1143
                Cancel cancel = new Cancel();
1144
                draw(image, g, cancel, scale);
1145
        }
1146

    
1147

    
1148
        /**
1149
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1150
         *
1151
         * @return the view port
1152
         *
1153
         * @see #setViewPort(ViewPort)
1154
         */
1155
        public ViewPort getViewPort() {
1156
                return viewPort;
1157
        }
1158

    
1159
        /**
1160
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1161
         *  of this map.</p>
1162
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1163
         *  adds the new one.</p>
1164
         *
1165
         * @param viewPort the viewPort
1166
         *
1167
         * @see #getViewPort()
1168
         */
1169
        public void setViewPort(ViewPort viewPort) {
1170
                if (this.viewPort != null) {
1171
                        this.viewPort.removeViewPortListener(eventBuffer);
1172
                }
1173

    
1174
                if (this.mapContextDrawer != null){
1175
                        this.mapContextDrawer.setViewPort(viewPort);
1176
                }
1177

    
1178
                this.viewPort = viewPort;
1179
                if (viewPort != null)
1180
                        viewPort.addViewPortListener(eventBuffer);
1181
        }
1182

    
1183
        /**
1184
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1185
         *
1186
         * @param extent the extent of the new zoom
1187
         */
1188
        public void zoomToExtent(Rectangle2D extent) {
1189
                if (extent!=null)
1190
                        getViewPort().setExtent(extent);
1191
        }
1192

    
1193
        /**
1194
         * <p>Returns the union of all extents of all layers of this map.</p>
1195
         *
1196
         * @return full extent of layers of this map
1197
         * @throws ReadDriverException if the driver fails reading.
1198
         *
1199
         * @see FLayers#getFullExtent()
1200
         */
1201
        public Rectangle2D getFullExtent() throws ReadDriverException {
1202
                return layers.getFullExtent();
1203
        }
1204

    
1205
        /**
1206
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1207
         * <ul>
1208
         * <li>XML entity of the internal {@link ViewPort ViewPort}.
1209
         * <li>XML entity of the internal {@link FLayers FLayers}.
1210
         * </ul>
1211
         *
1212
         * @return XMLEntity the XML entity
1213
         * @throws XMLException if there is any error creating the XML from the map.
1214
         *
1215
         * @see #createFromXML(XMLEntity)
1216
         * @see #createFromXML03(XMLEntity)
1217
         * @see ViewPort#getXMLEntity()
1218
         * @see FLayers#getXMLEntity()
1219
         */
1220
        public XMLEntity getXMLEntity() throws XMLException {
1221
                XMLEntity xml = new XMLEntity();
1222
                xml.putProperty("className", this.getClass().getName());
1223
                xml.addChild(viewPort.getXMLEntity());
1224
                xml.addChild(layers.getXMLEntity());
1225

    
1226
                return xml;
1227
        }
1228

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

    
1250
                return fmap;
1251
        }
1252

    
1253
        /**
1254
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1255
         *  with the data of the {@link ViewPort ViewPort} and
1256
         *  {@link FLayers FLayers}.</p>
1257
         *
1258
         * @param xml an XML entity
1259
         *
1260
         * @return the new <code>MapContext</code> instance
1261
         *
1262
         * @throws XMLException if there is any error creating the map from the XML.
1263
         *
1264
         * @see #getXMLEntity()
1265
         * @see #createFromXML03(XMLEntity)
1266
         * @see ViewPort#createFromXML(XMLEntity)
1267
         * @see FLayers#setXMLEntity(XMLEntity)
1268
         */
1269
        public static MapContext createFromXML(XMLEntity xml) throws XMLException {
1270
                ViewPort vp = ViewPort.createFromXML(xml.getChild(0));
1271
                MapContext fmap = new MapContext(vp);
1272
                fmap.layers.setXMLEntity(xml.getChild(1));
1273
                fmap.layers.setName("root layer");
1274
                return fmap;
1275
        }
1276

    
1277
        /**
1278
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1279
         *
1280
         * @param listener the new listener
1281
         *
1282
         * @return <code>true</code> if has added the listener successfully
1283
         *
1284
         * @see #removeAtomicEventListener(AtomicEventListener)
1285
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1286
         */
1287
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1288
                return eventBuffer.addAtomicEventListener(listener);
1289
        }
1290

    
1291
        /**
1292
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1293
         *
1294
         * @param listener the listener to remove
1295
         *
1296
     * @return <tt>true</tt> if the list contained the specified element
1297
         *
1298
         * @see #addAtomicEventListener(AtomicEventListener)
1299
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1300
         */
1301
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1302
                return eventBuffer.removeAtomicEventListener(listener);
1303
        }
1304

    
1305
        /**
1306
         * @see EventBuffer#beginAtomicEvent()
1307
         *
1308
         * @see #endAtomicEvent()
1309
         */
1310
        public void beginAtomicEvent() {
1311
                eventBuffer.beginAtomicEvent();
1312
        }
1313

    
1314
        /**
1315
         * @see EventBuffer#endAtomicEvent()
1316
         *
1317
         * @see #beginAtomicEvent()
1318
         */
1319
        public void endAtomicEvent() {
1320
                eventBuffer.endAtomicEvent();
1321
        }
1322

    
1323
        /**
1324
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1325
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1326
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1327
         *
1328
         * @author Fernando Gonz?lez Cort?s
1329
         */
1330
        public class LayerEventListener implements LayerCollectionListener {
1331
                /*
1332
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1333
                 */
1334
                public void layerAdded(LayerCollectionEvent e) {
1335
                        // Si es la primera capa, fijamos su extent al ViewPort
1336
                        // if (getLayers().getLayersCount() == 1) {
1337
                        if (getViewPort().getExtent() == null) {
1338
                                FLayer lyr = e.getAffectedLayer();
1339
                                if (lyr.isAvailable()) {
1340
                                        try {
1341
                                                getViewPort().setExtent(lyr.getFullExtent());
1342
                                        } catch (ReadDriverException e1) {
1343
                                                e1.printStackTrace();
1344
                                        }
1345
                                }
1346
                        }
1347

    
1348
                        // Registramos al FMap como listener del legend de las capas
1349
                        FLayer lyr = e.getAffectedLayer();
1350
                        selectionListener(lyr);
1351
                }
1352

    
1353
                /**
1354
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1355
                 *
1356
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1357
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1358
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1359
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1360
                 *
1361
                 * @param the layer or layers
1362
                 */
1363
                private void selectionListener(FLayer lyr){
1364
                        lyr.addLayerListener(eventBuffer);
1365

    
1366
                        if (lyr instanceof Classifiable) {
1367
                                Classifiable c = (Classifiable) lyr;
1368
                                c.addLegendListener(eventBuffer);
1369
                        }
1370

    
1371
                        if (lyr instanceof AlphanumericData) {
1372
                                Selectable s=null;
1373
                                try {
1374
                                        s = ((AlphanumericData) lyr).getRecordset();
1375
                                        if (s!=null) {
1376
                                                s.addSelectionListener(eventBuffer);
1377
                                        }
1378
                                } catch (ReadDriverException e1) {
1379
                                        e1.printStackTrace();
1380
                                }
1381

    
1382
                        }
1383
                        if (lyr instanceof FLayers){
1384
                                FLayers lyrs=(FLayers)lyr;
1385
                                for(int i=0;i<lyrs.getLayersCount();i++){
1386
                                        selectionListener(lyrs.getLayer(i));
1387
                                }
1388
                        }
1389

    
1390
                }
1391
                /*
1392
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1393
                 */
1394
                public void layerMoved(LayerPositionEvent e) {
1395
                }
1396

    
1397
                /*
1398
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1399
                 */
1400
                public void layerRemoved(LayerCollectionEvent e) {
1401
                        FLayer lyr = e.getAffectedLayer();
1402

    
1403
                        lyr.removeLayerListener(eventBuffer);
1404

    
1405
                        if (lyr instanceof Classifiable) {
1406
                                Classifiable c = (Classifiable) lyr;
1407
                                c.removeLegendListener(eventBuffer);
1408
                        }
1409

    
1410
                        if (lyr instanceof Selectable) {
1411
                                Selectable s = (Selectable) lyr;
1412
                                s.addSelectionListener(eventBuffer);
1413
                        }
1414
                }
1415

    
1416
                /*
1417
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1418
                 */
1419
                public void layerAdding(LayerCollectionEvent e)
1420
                                throws CancelationException {
1421
                        
1422
                    // before layer is added: ckeck name
1423
                    FLayer lyr = e.getAffectedLayer();
1424
                    String name = lyr.getName();
1425
                    name = LayerNameUtils.normalizeName(name);
1426
                    if (LayerNameUtils.nameExists(getLayers(), name)) {
1427
                            
1428
                            if (LayerNameUtils.getIndexFromName(name) != null) {
1429
                                    name = LayerNameUtils.removeIndex(name);
1430
                            }
1431
                            
1432
                            long high_ind =
1433
                                    LayerNameUtils.findHighestIndex(getLayers(), name);
1434
                            
1435
                            if (high_ind == 0) {
1436
                                    high_ind = 2;
1437
                            } else {
1438
                                    high_ind++;
1439
                            }
1440
                            
1441
                            name = LayerNameUtils.composeWithIndex(name, high_ind);
1442
                            lyr.setName(name);
1443
                    }
1444

    
1445
                }
1446

    
1447
                /*
1448
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1449
                 */
1450
                public void layerMoving(LayerPositionEvent e)
1451
                                throws CancelationException {
1452
                }
1453

    
1454
                /*
1455
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1456
                 */
1457
                public void layerRemoving(LayerCollectionEvent e)
1458
                                throws CancelationException {
1459
                }
1460

    
1461

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

    
1470
        /**
1471
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1472
         *  collection of layers argument.</p>
1473
         *
1474
         * @param a collection of layers
1475
         */
1476
        public void addAsCollectionListener(FLayers layers2) {
1477
                layers2.addLayerCollectionListener(layerEventListener);
1478
        }
1479

    
1480
        /**
1481
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1482
         *
1483
         * @return the graphic layer of this map
1484
         *
1485
         * @see #setGraphicsLayer(GraphicLayer)
1486
         */
1487
        public GraphicLayer getGraphicsLayer() {
1488
                return tracLayer;
1489
        }
1490

    
1491
        /**
1492
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1493
         *
1494
         * @param graphicLayer the new graphic layer
1495
         *
1496
         * @see #getGraphicsLayer()
1497
         */
1498
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1499
                tracLayer = graphicLayer;
1500
        }
1501

    
1502
        /**
1503
         * <p>Indicates whether some other object is "equal to" this map.</p>
1504
         * <p>Returns <code>true</code> if success one of this options:
1505
         * <ul>
1506
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1507
         * <li>Both maps have the same layers.
1508
         * <li>Both maps have the same number of layers and with the same name.
1509
         * </ul>
1510
         * </p>
1511
         *
1512
         * @param obj the reference object with which to compare.
1513
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1514
         *
1515
         * @see Object#equals(Object)
1516
         */
1517
        public boolean equals(Object arg0) {
1518
                if (!(arg0 instanceof MapContext)){
1519
                        return false;
1520
                }
1521
                MapContext map = (MapContext) arg0;
1522
                if (super.equals(arg0))
1523
                        return true;
1524
                if (getLayers() == map.getLayers())
1525
                        return true;
1526
                boolean isEqual = true;
1527
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1528
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1529

    
1530
                                if (!getLayers().getLayer(i).getName().equals(
1531
                                                map.getLayers().getLayer(i).getName())) {
1532
                                        isEqual = false;
1533
                                }
1534

    
1535
                        }
1536
                } else {
1537
                        isEqual = false;
1538
                }
1539
                return isEqual;
1540
        }
1541

    
1542
        /**
1543
         * <p>Registers the message of an error associated to this map.</p>
1544
         *
1545
         * @param stringProperty the error message
1546
         *
1547
         * @see #getLayersError()
1548
         * @see #clearErrors()
1549
         */
1550
        public void addLayerError(String stringProperty) {
1551
                layersError.add(stringProperty);
1552
        }
1553

    
1554
        /**
1555
         * <p>Gets the list with all error messages registered to this map.</p>
1556
         *
1557
         * @return the list of errors registered to this map
1558
         *
1559
         * @see #addLayerError(String)
1560
         * @see #clearErrors()
1561
         */
1562
        public ArrayList getLayersError() {
1563
                return layersError;
1564
        }
1565

    
1566
        /**
1567
         * <p>Removes all error messages associated to this map.</p>
1568
         *
1569
         * @see #addLayerError(String)
1570
         * @see #getLayersError()
1571
         */
1572
        public void clearErrors() {
1573
                layersError.clear();
1574
        }
1575

    
1576
        /**
1577
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1578
         *
1579
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1580
         * @return the new layer node
1581
         */
1582
        public FLayers getNewGroupLayer(FLayers parent) {
1583
                FLayers group1 = new FLayers();//(this,parent);
1584
                group1.setMapContext(this);
1585
                group1.setParentLayer(parent);
1586
            return group1;
1587
        }
1588

    
1589

    
1590
        public long getDrawVersion() {
1591
                return this.drawVersion;
1592
        }
1593

    
1594
        protected void updateDrawVersion(){
1595
                this.drawVersion++;
1596
        }
1597

    
1598
        public MapContextDrawer getMapContextDrawer() throws ReadDriverException{
1599
                if (this.mapContextDrawer == null){
1600
                        try {
1601
                                this.mapContextDrawer = (MapContextDrawer) this.mapContextDrawerClass.getConstructor(null).newInstance(null);
1602

    
1603
                        } catch (IllegalArgumentException e) {
1604
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1605
                        } catch (SecurityException e) {
1606
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1607
                        } catch (InstantiationException e) {
1608
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1609
                        } catch (IllegalAccessException e) {
1610
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1611
                        } catch (InvocationTargetException e) {
1612
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1613
                        } catch (NoSuchMethodException e) {
1614
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1615
                        }
1616
                }
1617
                this.mapContextDrawer.setMapContext(this);
1618
                this.mapContextDrawer.setViewPort(viewPort);
1619

    
1620
                return this.mapContextDrawer;
1621
        }
1622

    
1623
        public boolean setMapContextDrawerClass(Class mapContextDrawerClass){
1624
                if (mapContextDrawerClass == null){
1625
                        mapContextDrawerClass = this.defaultMapContextDrawerClass;
1626
                }
1627
                if (! MapContextDrawer.class.isAssignableFrom(mapContextDrawerClass)){
1628
                        return false;
1629
                }
1630
                this.mapContextDrawerClass = mapContextDrawerClass;
1631
                if (this.mapContextDrawer != null){
1632
                        this.mapContextDrawer.dispose();
1633
                        this.mapContextDrawer = null;
1634
                }
1635
                return true;
1636

    
1637
        }
1638

    
1639
        public void setMapContextDrawer(MapContextDrawer drawer){
1640
                if (this.mapContextDrawer != null){
1641
                        this.mapContextDrawer.dispose();
1642
                        this.mapContextDrawer = null;
1643
                }
1644
                this.mapContextDrawer = drawer;
1645
                if (this.mapContextDrawer != null){
1646
                        this.mapContextDrawer.setMapContext(this);
1647
                        this.mapContextDrawer.setViewPort(viewPort);
1648
                }
1649
        }
1650

    
1651
        /**
1652
         * <p>Returns the OrderManager. When a new layer is added to this MapContext,
1653
         * the order manager will decide the right position for the
1654
         * layer.</p> <p>The orderManager will only be used when adding a layer,
1655
         * so it will not affect to the collection order when the user modifies the
1656
         * order.</p>
1657
         *
1658
         * @return The configured order manager, or DefaultOrderManager in no
1659
         * order manager was configured
1660
         */
1661
        public OrderManager getOrderManager() {
1662
                /**
1663
                 * Don't keep an order manager, always use a system-wide manager.
1664
                 *
1665
                 * In an idea scenario, we'd like to be able to configure an order
1666
                 * manager at different levels (system-wide, project-wide,
1667
                 * mapcontext-wide).
1668
                 *
1669
                 * However, gvSIG 1.9 does not provide the necessary infraestructure
1670
                 * to support this behaviour in a clean-way, so we don't offer this
1671
                 * behaviour at this time.
1672
                 */
1673
                return DefaultOrderManager.getDefaultOrderManager();
1674
        }
1675
}