Statistics
| Revision:

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

History | View | Annotate | Download (54.2 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.selection.Record;
83
import com.iver.cit.gvsig.fmap.operations.strategies.FeatureVisitor;
84
import com.iver.cit.gvsig.fmap.operations.strategies.SelectedZoomVisitor;
85
import com.iver.cit.gvsig.fmap.rendering.LegendListener;
86
import com.iver.utiles.XMLEntity;
87
import com.iver.utiles.swing.threads.Cancellable;
88

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

    
147

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

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

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

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

    
177

    
178
    }
179

    
180

    
181

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

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

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

    
296
        public static final int EQUALS = 0;
297

    
298
        public static final int DISJOINT = 1;
299

    
300
        public static final int INTERSECTS = 2;
301

    
302
        public static final int TOUCHES = 3;
303

    
304
        public static final int CROSSES = 4;
305

    
306
        public static final int WITHIN = 5;
307

    
308
        public static final int CONTAINS = 6;
309

    
310
        public static final int OVERLAPS = 7;
311

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

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

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

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

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

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

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

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

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

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

    
401

    
402

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

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

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

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

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

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

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

    
445

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

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

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

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

    
478
                setViewPort(vp);
479

    
480
        }
481

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

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

    
495
                setViewPort(vp);
496
        }
497

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
715
                Cancellable cancel = new Cancellable() {
716
                        public boolean isCanceled() {
717
                                return false;
718
                        }
719

    
720
                        public void setCanceled(boolean canceled) {
721
                                // No queremos que se pueda cancelar la impresi?n.
722

    
723
                        }
724
                };
725
                this.getMapContextDrawer().print(this.layers, g, cancel, scale,properties);
726
                tracLayer.draw(null, g, viewPort, cancel, scale);
727
        }
728

    
729
        /**
730
         * <p>Returns a new <code>MapContext</code> instance with the information of the <code>vp</code> argument, and the layers of this map.</p>
731
         *
732
         * @param vp information for drawing the layers of this map in the available rectangular area according a projection
733
         *
734
         * @return a new <code>MapContext</code> instance projected by <code>vp</code>
735
         */
736
        public MapContext createNewFMap(ViewPort vp) {
737
                MapContext ret = new MapContext(vp);
738
                ret.layers = this.layers;
739

    
740
                return ret;
741
        }
742

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

    
775
                
776
                return ret;
777

    
778
//                return createFromXML(getXMLEntity());
779

    
780
        }
781

    
782
        /**
783
         * Like {@linkplain #cloneFMap()}, but now doesn't clone the layers, rather copies them.
784
         *
785
         * @return the new map
786
         */
787
        public MapContext cloneToDraw() {
788
                ViewPort vp = getViewPort().cloneViewPort();
789
                MapContext mapContext=new MapContext(getLayers(),vp);
790
                return mapContext;
791
        }
792

    
793
        /**
794
         * A?ade la capa que se pasa como par?metro al nodo que se pasa como
795
         * parametro y lanza ProjectionMismatchException si no est?n todas las capas
796
         * de este FMap en la misma proyecci?n. Lanza un ChildNotAllowedException si
797
         * la capa no es un FLayers y no permite hijos
798
         *
799
         * @param vectorial
800
         *            DOCUMENT ME!
801
         */
802

    
803
        /*
804
         * public void addLayer(LayerPath parent, FLayer layer) throws
805
         * ProjectionMismatchException, ChildrenNotAllowedException {
806
         * layers.addLayer(parent, layer); } public void removeLayer(LayerPath
807
         * parent)throws ChildrenNotAllowedException{ layers.removeLayer(parent); }
808
         */
809

    
810
        /**
811
         * <p>Adds a layer to the group of layers that are at a upper level in the tree.</p>
812
         *
813
         * @param vectorial the layer to add
814
         */
815
        public void addToTrackLayer(FLayer vectorial) {
816
        }
817

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

    
836
                if (viewPort.getImageSize() == null)
837
                        return -1;
838

    
839
                if (viewPort.getAdjustedExtent() == null) {
840
                        return 0;
841
                }
842
                double[] trans2Meter=getDistanceTrans2Meter();
843
                if (proj == null) {
844
                        double w = ((viewPort.getImageSize().getWidth() / dpi) * 2.54);
845
                        return (long) (viewPort.getAdjustedExtent().getWidth() / w * trans2Meter[getViewPort()
846
                                        .getMapUnits()]);
847
                }
848

    
849
                return Math.round(proj.getScale((viewPort.getAdjustedExtent().getMinX()*trans2Meter[getViewPort().getMapUnits()]),
850
                                (viewPort.getAdjustedExtent().getMaxX()*trans2Meter[getViewPort().getMapUnits()]), viewPort.getImageSize()
851
                                                .getWidth(), dpi));
852

    
853

    
854
        }
855

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

    
886
        /**
887
         * <p>Returns the screen resolution (Dots Per Inch) as it was defined by the user's preference, or
888
         * by default as it is defined in the default Toolkit.</p>
889
         *
890
         * @return double with the screen's dpi
891
         */
892
        public static double getScreenDPI() {
893
                Preferences prefsResolution = Preferences.userRoot().node( "gvsig.configuration.screen" );
894
                Toolkit kit = Toolkit.getDefaultToolkit();
895
                double dpi = prefsResolution.getInt("dpi",kit.getScreenResolution());
896
                return dpi;
897
        }
898

    
899
        /**
900
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#setVectorial(com.iver.cit.gvsig.fmap.VectorialAdapter)
901
         */
902
        public void setVectorial(VectorialAdapter v) {
903
        }
904

    
905
        /**
906
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#process(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor)
907
         */
908
        public void process(FeatureVisitor visitor) {
909
        }
910

    
911
        /**
912
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#processSelected(com.iver.cit.gvsig.fmap.FeatureVisitor)
913
         */
914
        public void processSelected(FeatureVisitor visitor) {
915
        }
916

    
917
        /**
918
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#select(com.iver.cit.gvsig.fmap.FeatureSelectorVisitor,
919
         *      VectorialSubSet)
920
         */
921
        public void select(FeatureVisitor visitor) {
922
        }
923

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

    
930
        /**
931
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#createIndex()
932
         */
933
        public void createIndex() {
934
        }
935

    
936
        /**
937
         * @see org.cresques.geo.Projected#getProjection()
938
         *
939
         * @see ViewPort#getProjection()
940
         * @see #setProjection(IProjection)
941
         * @see #reProject(ICoordTrans)
942
         */
943
        public IProjection getProjection() {
944
                return getViewPort().getProjection();
945
        }
946

    
947
        /**
948
         * <p>Sets the new projection.</p>
949
         *
950
         * @param proj the new projection
951
         *
952
         * @see #getProjection()
953
         * @see ViewPort#setProjection(IProjection)
954
         * @see #reProject(ICoordTrans)
955
         */
956
        public void setProjection(IProjection proj) {
957
                if (getViewPort() != null) {
958
                        getViewPort().setProjection(proj);
959
                }
960
        }
961

    
962
        /**
963
         * @see org.cresques.geo.Projected#reProject(org.cresques.cts.ICoordTrans)
964
         */
965
        public void reProject(ICoordTrans arg0) {
966
                // TODO implementar reprojecci?n (lo que sea eso)
967
        }
968

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

    
985
        /**
986
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByRect(java.awt.geom.Rectangle2D)
987
         */
988
        /*
989
         * public void selectByRect(Rectangle2D rect) throws DriverException {
990
         * FLayer[] actives = layers.getActives(); for (int i=0; i < actives.length;
991
         * i++) { if (actives[i] instanceof FLyrVect) { FLyrVect lyrVect =
992
         * (FLyrVect) actives[i]; FBitSet oldBitSet = lyrVect.getSelection();
993
         * FBitSet newBitSet = lyrVect.queryByRect(rect); newBitSet.xor(oldBitSet);
994
         * lyrVect.setSelection(newBitSet); } }
995
         *  }
996
         */
997

    
998
        /**
999
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#selectByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
1000
         *      int)
1001
         */
1002
        public void selectByShape(IGeometry g, int relationship) {
1003
        }
1004

    
1005
        /**
1006
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByPoint(Point2D,
1007
         *      double)
1008
         */
1009
        public Record[] queryByPoint(Point2D p, double tolerance) {
1010
                return null;
1011
        }
1012

    
1013
        /**
1014
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByRect(java.awt.geom.Rectangle2D)
1015
         */
1016
        public Record[] queryByRect(Rectangle2D rect) {
1017
                return null;
1018
        }
1019

    
1020
        /**
1021
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#queryByShape(com.iver.cit.gvsig.fmap.fshape.IGeometry,
1022
         *      int)
1023
         */
1024
        public Record[] queryByShape(IGeometry g, int relationship) {
1025
                return null;
1026
        }
1027

    
1028
        /**
1029
         * @see com.iver.cit.gvsig.fmap.operations.strategies.Strategy#getSelectionBounds()
1030
         *
1031
         * @see SelectedZoomVisitor#getSelectBound()
1032
         */
1033
        public Rectangle2D getSelectionBounds() {
1034
                SelectedZoomVisitor visitor = new SelectedZoomVisitor();
1035

    
1036
                try {
1037
                        layers.process(visitor);
1038
                } catch (ReadDriverException e1) {
1039
                        throw new RuntimeException(
1040
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
1041
                                        e1);
1042
                } catch (VisitorException e) {
1043
                        throw new RuntimeException(
1044
                                        "No se espera que SelectByPointVisitor lance esta excepci?n",
1045
                                        e);
1046
                }
1047

    
1048
                return visitor.getSelectBound();
1049
        }
1050

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

    
1081
//                prepareDrawing(image, g, scale);
1082

    
1083
                // M?s c?lidad al texto
1084
                RenderingHints renderHints = new RenderingHints(
1085
                                RenderingHints.KEY_ANTIALIASING,
1086
                                RenderingHints.VALUE_ANTIALIAS_ON);
1087
                renderHints.put(RenderingHints.KEY_RENDERING,
1088
                                RenderingHints.VALUE_RENDER_QUALITY);
1089
                renderHints.put(RenderingHints.KEY_TEXT_ANTIALIASING,
1090
                                RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
1091
                g.setRenderingHints(renderHints);
1092

    
1093
                long t1 = System.currentTimeMillis();
1094
//                layers.draw(image, g, viewPort, cancel, scale);
1095

    
1096
                this.getMapContextDrawer().draw(this.layers, image, g, cancel, scale);
1097

    
1098
                LayerDrawEvent beforeTracLayerEvent = new LayerDrawEvent(tracLayer,
1099
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_BEFORE_DRAW);
1100
                fireLayerDrawingEvent(beforeTracLayerEvent);
1101
                tracLayer.draw(image, g, viewPort, cancel, scale);
1102
                LayerDrawEvent afterTracLayerEvent = new LayerDrawEvent(tracLayer,
1103
                                g, viewPort, LayerDrawEvent.GRAPHICLAYER_AFTER_DRAW);
1104
                fireLayerDrawingEvent(afterTracLayerEvent);
1105

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

    
1136
        /**
1137
         * <p>Like {@linkplain MapContext#draw(BufferedImage, Graphics2D, Cancellable, double)}, but creating
1138
         *  the task as cancellable.</p>
1139
         *
1140
         * @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.
1141
         * @param g for rendering 2-dimensional shapes, text and images on the Java(tm) platform
1142
         * @param scale the scale of the view. Must be between {@linkplain FLayer#getMinScale()} and {@linkplain FLayer#getMaxScale()}.
1143
         *
1144
         * @throws ReadDriverException if the driver fails reading.
1145
         *
1146
         * @see #draw(BufferedImage, Graphics2D, Cancellable, double)
1147
         */
1148
        public void draw(BufferedImage image, Graphics2D g, double scale)
1149
                        throws ReadDriverException {
1150
//                layers.setDirty(true);
1151
                draw(image, g, new Cancellable() {
1152
                        /**
1153
                         * @see com.iver.utiles.swing.threads.Cancellable#isCanceled()
1154
                         */
1155
                        public boolean isCanceled() {
1156
                                return false;
1157
                        }
1158

    
1159
                        public void setCanceled(boolean canceled) {
1160
                                // TODO Auto-generated method stub
1161

    
1162
                        }
1163
                }, scale);
1164
        }
1165

    
1166

    
1167
        /**
1168
         * <p>Gets the {@link ViewPort ViewPort} associated to this map.</p>
1169
         *
1170
         * @return the view port
1171
         *
1172
         * @see #setViewPort(ViewPort)
1173
         */
1174
        public ViewPort getViewPort() {
1175
                return viewPort;
1176
        }
1177

    
1178
        /**
1179
         * <p>Sets a {@link ViewPort ViewPort} with the drawing information
1180
         *  of this map.</p>
1181
         * <p>If there was a previous view port, removes its {@link EventBuffer EventBuffer} and
1182
         *  adds the new one.</p>
1183
         *
1184
         * @param viewPort the viewPort
1185
         *
1186
         * @see #getViewPort()
1187
         */
1188
        public void setViewPort(ViewPort viewPort) {
1189
                if (this.viewPort != null) {
1190
                        this.viewPort.removeViewPortListener(eventBuffer);
1191
                }
1192

    
1193
                if (this.mapContextDrawer != null){
1194
                        this.mapContextDrawer.setViewPort(viewPort);
1195
                }
1196

    
1197
                this.viewPort = viewPort;
1198
                if (viewPort != null)
1199
                        viewPort.addViewPortListener(eventBuffer);
1200
        }
1201

    
1202
        /**
1203
         * <p>Sets the given extent to the {@link ViewPort ViewPort} and updates the view with the new zoom.</p>
1204
         *
1205
         * @param extent the extent of the new zoom
1206
         */
1207
        public void zoomToExtent(Rectangle2D extent) {
1208
                if (extent!=null)
1209
                        getViewPort().setExtent(extent);
1210
        }
1211

    
1212
        /**
1213
         * <p>Returns the union of all extents of all layers of this map.</p>
1214
         *
1215
         * @return full extent of layers of this map
1216
         * @throws ReadDriverException if the driver fails reading.
1217
         *
1218
         * @see FLayers#getFullExtent()
1219
         */
1220
        public Rectangle2D getFullExtent() throws ReadDriverException {
1221
                return layers.getFullExtent();
1222
        }
1223

    
1224
        /**
1225
         * <p>Returns an XML entity with the name of this class as a property, and two children branches:<br>
1226
         * <ul>
1227
         * <li>XML entity of the internal {@link ViewPort ViewPort}.
1228
         * <li>XML entity of the internal {@link FLayers FLayers}.
1229
         * </ul>
1230
         *
1231
         * @return XMLEntity the XML entity
1232
         * @throws XMLException if there is any error creating the XML from the map.
1233
         *
1234
         * @see #createFromXML(XMLEntity)
1235
         * @see #createFromXML03(XMLEntity)
1236
         * @see ViewPort#getXMLEntity()
1237
         * @see FLayers#getXMLEntity()
1238
         */
1239
        public XMLEntity getXMLEntity() throws XMLException {
1240
                XMLEntity xml = new XMLEntity();
1241
                xml.putProperty("className", this.getClass().getName());
1242
                xml.addChild(viewPort.getXMLEntity());
1243
                xml.addChild(layers.getXMLEntity());
1244

    
1245
                return xml;
1246
        }
1247

    
1248
        /**
1249
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1250
         *  the data of the {@link ViewPort ViewPort} and
1251
         *  {@link FLayers FLayers}.</p>
1252
         *
1253
         * @param xml an XML entity
1254
         *
1255
         * @return the new <code>MapContext</code> instance
1256
         *
1257
         * @throws XMLException if there is any error creating the map from the XML.
1258
         *
1259
         * @see #getXMLEntity()
1260
         * @see #createFromXML(XMLEntity)
1261
         * @see ViewPort#createFromXML03(XMLEntity)
1262
         * @see FLayers#setXMLEntity03(XMLEntity)
1263
         */
1264
        public static MapContext createFromXML03(XMLEntity xml) throws XMLException {
1265
                ViewPort vp = ViewPort.createFromXML03(xml.getChild(0));
1266
                MapContext fmap = new MapContext(vp);
1267
                fmap.layers.setXMLEntity03(xml.getChild(1));
1268

    
1269
                return fmap;
1270
        }
1271

    
1272
        /**
1273
         * <p>Creates a new <code>MapContext</code> instance from an XML entity, with
1274
         *  with the data of the {@link ViewPort ViewPort} and
1275
         *  {@link FLayers FLayers}.</p>
1276
         *
1277
         * @param xml an XML entity
1278
         *
1279
         * @return the new <code>MapContext</code> instance
1280
         *
1281
         * @throws XMLException if there is any error creating the map from the XML.
1282
         *
1283
         * @see #getXMLEntity()
1284
         * @see #createFromXML03(XMLEntity)
1285
         * @see ViewPort#createFromXML(XMLEntity)
1286
         * @see FLayers#setXMLEntity(XMLEntity)
1287
         */
1288
        public static MapContext createFromXML(XMLEntity xml) throws XMLException {
1289
                ViewPort vp = ViewPort.createFromXML(xml.getChild(0));
1290
                MapContext fmap = new MapContext(vp);
1291
                fmap.layers.setXMLEntity(xml.getChild(1));
1292
                fmap.layers.setName("root layer");
1293
                return fmap;
1294
        }
1295

    
1296
        /**
1297
         * <p>Adds a listener of atomic events to the internal {@link EventBuffer EventBuffer}.</p>
1298
         *
1299
         * @param listener the new listener
1300
         *
1301
         * @return <code>true</code> if has added the listener successfully
1302
         *
1303
         * @see #removeAtomicEventListener(AtomicEventListener)
1304
         * @see EventBuffer#addAtomicEventListener(AtomicEventListener)
1305
         */
1306
        public boolean addAtomicEventListener(AtomicEventListener listener) {
1307
                return eventBuffer.addAtomicEventListener(listener);
1308
        }
1309

    
1310
        /**
1311
         * <p>Removes a listener of atomic events from the internal {@link EventBuffer EventBuffer}.</p>
1312
         *
1313
         * @param listener the listener to remove
1314
         *
1315
     * @return <tt>true</tt> if the list contained the specified element
1316
         *
1317
         * @see #addAtomicEventListener(AtomicEventListener)
1318
         * @see EventBuffer#removeAtomicEventListener(AtomicEventListener)
1319
         */
1320
        public boolean removeAtomicEventListener(AtomicEventListener listener) {
1321
                return eventBuffer.removeAtomicEventListener(listener);
1322
        }
1323

    
1324
        /**
1325
         * @see EventBuffer#beginAtomicEvent()
1326
         *
1327
         * @see #endAtomicEvent()
1328
         */
1329
        public void beginAtomicEvent() {
1330
                eventBuffer.beginAtomicEvent();
1331
        }
1332

    
1333
        /**
1334
         * @see EventBuffer#endAtomicEvent()
1335
         *
1336
         * @see #beginAtomicEvent()
1337
         */
1338
        public void endAtomicEvent() {
1339
                eventBuffer.endAtomicEvent();
1340
        }
1341

    
1342
        /**
1343
         * <p>The class <code>LayerEventListener</code> implements the methods of {@link LayerCollectionListener LayerCollectionListener}
1344
         *  that handles the "layer added" or "layer removed" events in a map.</p>
1345
         * <p>Is designed as a listener for all layers in a {@link MapContext MapContext}.</p>
1346
         *
1347
         * @author Fernando Gonz?lez Cort?s
1348
         */
1349
        public class LayerEventListener implements LayerCollectionListener {
1350
                /*
1351
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdded(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1352
                 */
1353
                public void layerAdded(LayerCollectionEvent e) {
1354
                        // Si es la primera capa, fijamos su extent al ViewPort
1355
                        // if (getLayers().getLayersCount() == 1) {
1356
                        if (getViewPort().getExtent() == null) {
1357
                                FLayer lyr = e.getAffectedLayer();
1358
                                if (lyr.isAvailable()) {
1359
                                        try {
1360
                                                getViewPort().setExtent(lyr.getFullExtent());
1361
                                        } catch (ReadDriverException e1) {
1362
                                                e1.printStackTrace();
1363
                                        }
1364
                                }
1365
                        }
1366

    
1367
                        // Registramos al FMap como listener del legend de las capas
1368
                        FLayer lyr = e.getAffectedLayer();
1369
                        selectionListener(lyr);
1370
                }
1371

    
1372
                /**
1373
                 * <p>Registers an event buffer as a listener for all layers as argument.</p>
1374
                 *
1375
                 * <p>Each {@link FLayer FLayer} of this map must have an event buffer for all kind
1376
                 * of specific listeners of that layer. This method distinguish between {@link Classifiable Classifiable},
1377
                 * {@link AlphanumericData AlphanumericData}, and {@link FLayers FLayers} layers, and for each one,
1378
                 * registers, for their specific listeners, the <code>eventBuffer</code> as a listener.</p>
1379
                 *
1380
                 * @param the layer or layers
1381
                 */
1382
                private void selectionListener(FLayer lyr){
1383
                        lyr.addLayerListener(eventBuffer);
1384

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

    
1390
                        if (lyr instanceof AlphanumericData) {
1391
                                Selectable s=null;
1392
                                try {
1393
                                        s = ((AlphanumericData) lyr).getRecordset();
1394
                                        if (s!=null) {
1395
                                                s.addSelectionListener(eventBuffer);
1396
                                        }
1397
                                } catch (ReadDriverException e1) {
1398
                                        e1.printStackTrace();
1399
                                }
1400

    
1401
                        }
1402
                        if (lyr instanceof FLayers){
1403
                                FLayers lyrs=(FLayers)lyr;
1404
                                for(int i=0;i<lyrs.getLayersCount();i++){
1405
                                        selectionListener(lyrs.getLayer(i));
1406
                                }
1407
                        }
1408

    
1409
                }
1410
                /*
1411
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoved(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1412
                 */
1413
                public void layerMoved(LayerPositionEvent e) {
1414
                }
1415

    
1416
                /*
1417
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoved(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1418
                 */
1419
                public void layerRemoved(LayerCollectionEvent e) {
1420
                        FLayer lyr = e.getAffectedLayer();
1421

    
1422
                        lyr.removeLayerListener(eventBuffer);
1423

    
1424
                        if (lyr instanceof Classifiable) {
1425
                                Classifiable c = (Classifiable) lyr;
1426
                                c.removeLegendListener(eventBuffer);
1427
                        }
1428

    
1429
                        if (lyr instanceof Selectable) {
1430
                                Selectable s = (Selectable) lyr;
1431
                                s.addSelectionListener(eventBuffer);
1432
                        }
1433
                }
1434

    
1435
                /*
1436
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerAdding(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1437
                 */
1438
                public void layerAdding(LayerCollectionEvent e)
1439
                                throws CancelationException {
1440
                        
1441
                    // before layer is added: ckeck name
1442
                    FLayer lyr = e.getAffectedLayer();
1443
                    String name = lyr.getName();
1444
                    name = LayerNameUtils.normalizeName(name);
1445
                    if (LayerNameUtils.nameExists(getLayers(), name)) {
1446
                            
1447
                            if (LayerNameUtils.getIndexFromName(name) != null) {
1448
                                    name = LayerNameUtils.removeIndex(name);
1449
                            }
1450
                            
1451
                            long high_ind =
1452
                                    LayerNameUtils.findHighestIndex(getLayers(), name);
1453
                            
1454
                            if (high_ind == 0) {
1455
                                    high_ind = 2;
1456
                            } else {
1457
                                    high_ind++;
1458
                            }
1459
                            
1460
                            name = LayerNameUtils.composeWithIndex(name, high_ind);
1461
                            lyr.setName(name);
1462
                    }
1463

    
1464
                }
1465

    
1466
                /*
1467
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerMoving(com.iver.cit.gvsig.fmap.layers.LayerPositionEvent)
1468
                 */
1469
                public void layerMoving(LayerPositionEvent e)
1470
                                throws CancelationException {
1471
                }
1472

    
1473
                /*
1474
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#layerRemoving(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1475
                 */
1476
                public void layerRemoving(LayerCollectionEvent e)
1477
                                throws CancelationException {
1478
                }
1479

    
1480

    
1481
                /*
1482
                 * @see com.iver.cit.gvsig.fmap.layers.LayerCollectionListener#visibilityChanged(com.iver.cit.gvsig.fmap.layers.LayerCollectionEvent)
1483
                 */
1484
                public void visibilityChanged(LayerCollectionEvent e)
1485
                                throws CancelationException {
1486
                }
1487
        }
1488

    
1489
        /**
1490
         * <p>Adds the {@link LayerEventListener LayerEventListener} of this map to the
1491
         *  collection of layers argument.</p>
1492
         *
1493
         * @param a collection of layers
1494
         */
1495
        public void addAsCollectionListener(FLayers layers2) {
1496
                layers2.addLayerCollectionListener(layerEventListener);
1497
        }
1498

    
1499
        /**
1500
         * <p>Returns the internal {@link GraphicLayer GraphicLayer}.</p>
1501
         *
1502
         * @return the graphic layer of this map
1503
         *
1504
         * @see #setGraphicsLayer(GraphicLayer)
1505
         */
1506
        public GraphicLayer getGraphicsLayer() {
1507
                return tracLayer;
1508
        }
1509

    
1510
        /**
1511
         * <p>Sets a new {@link GraphicLayer GraphicLayer} to this map.</p>
1512
         *
1513
         * @param graphicLayer the new graphic layer
1514
         *
1515
         * @see #getGraphicsLayer()
1516
         */
1517
        public void setGraphicsLayer(GraphicLayer graphicLayer) {
1518
                tracLayer = graphicLayer;
1519
        }
1520

    
1521
        /**
1522
         * <p>Indicates whether some other object is "equal to" this map.</p>
1523
         * <p>Returns <code>true</code> if success one of this options:
1524
         * <ul>
1525
         * <li>Both objects are equal according to {@linkplain Object#equals(Object)}.
1526
         * <li>Both maps have the same layers.
1527
         * <li>Both maps have the same number of layers and with the same name.
1528
         * </ul>
1529
         * </p>
1530
         *
1531
         * @param obj the reference object with which to compare.
1532
     * @return <code>true</code> if this object is the same as the <code>arg0</code> argument;  otherwise <code>false</code>.
1533
         *
1534
         * @see Object#equals(Object)
1535
         */
1536
        public boolean equals(Object arg0) {
1537
                if (!(arg0 instanceof MapContext)){
1538
                        return false;
1539
                }
1540
                MapContext map = (MapContext) arg0;
1541
                if (super.equals(arg0))
1542
                        return true;
1543
                if (getLayers() == map.getLayers())
1544
                        return true;
1545
                boolean isEqual = true;
1546
                if (map.getLayers().getLayersCount() == getLayers().getLayersCount()) {
1547
                        for (int i = 0; i < getLayers().getLayersCount(); i++) {
1548

    
1549
                                if (!getLayers().getLayer(i).getName().equals(
1550
                                                map.getLayers().getLayer(i).getName())) {
1551
                                        isEqual = false;
1552
                                }
1553

    
1554
                        }
1555
                } else {
1556
                        isEqual = false;
1557
                }
1558
                return isEqual;
1559
        }
1560

    
1561
        /**
1562
         * <p>Registers the message of an error associated to this map.</p>
1563
         *
1564
         * @param stringProperty the error message
1565
         *
1566
         * @see #getLayersError()
1567
         * @see #clearErrors()
1568
         */
1569
        public void addLayerError(String stringProperty) {
1570
                layersError.add(stringProperty);
1571
        }
1572

    
1573
        /**
1574
         * <p>Gets the list with all error messages registered to this map.</p>
1575
         *
1576
         * @return the list of errors registered to this map
1577
         *
1578
         * @see #addLayerError(String)
1579
         * @see #clearErrors()
1580
         */
1581
        public ArrayList getLayersError() {
1582
                return layersError;
1583
        }
1584

    
1585
        /**
1586
         * <p>Removes all error messages associated to this map.</p>
1587
         *
1588
         * @see #addLayerError(String)
1589
         * @see #getLayersError()
1590
         */
1591
        public void clearErrors() {
1592
                layersError.clear();
1593
        }
1594

    
1595
        /**
1596
         * <p>Creates and returns a new group of layers that belongs to this <code>MapContext</code>.</p>
1597
         *
1598
         * @param parent layer node in this <code>MapContexte</code> that will be the parent of the new node
1599
         * @return the new layer node
1600
         */
1601
        public FLayers getNewGroupLayer(FLayers parent) {
1602
                FLayers group1 = new FLayers();//(this,parent);
1603
                group1.setMapContext(this);
1604
                group1.setParentLayer(parent);
1605
            return group1;
1606
        }
1607

    
1608

    
1609
        public long getDrawVersion() {
1610
                return this.drawVersion;
1611
        }
1612

    
1613
        protected void updateDrawVersion(){
1614
                this.drawVersion++;
1615
        }
1616

    
1617
        public MapContextDrawer getMapContextDrawer() throws ReadDriverException{
1618
                if (this.mapContextDrawer == null){
1619
                        try {
1620
                                this.mapContextDrawer = (MapContextDrawer) this.mapContextDrawerClass.getConstructor(null).newInstance(null);
1621

    
1622
                        } catch (IllegalArgumentException e) {
1623
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1624
                        } catch (SecurityException e) {
1625
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1626
                        } catch (InstantiationException e) {
1627
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1628
                        } catch (IllegalAccessException e) {
1629
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1630
                        } catch (InvocationTargetException e) {
1631
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1632
                        } catch (NoSuchMethodException e) {
1633
                                throw new ReadDriverException("Can't create MapContextDraver",e);
1634
                        }
1635
                }
1636
                this.mapContextDrawer.setMapContext(this);
1637
                this.mapContextDrawer.setViewPort(viewPort);
1638

    
1639
                return this.mapContextDrawer;
1640
        }
1641

    
1642
        public boolean setMapContextDrawerClass(Class mapContextDrawerClass){
1643
                if (mapContextDrawerClass == null){
1644
                        mapContextDrawerClass = this.defaultMapContextDrawerClass;
1645
                }
1646
                if (! MapContextDrawer.class.isAssignableFrom(mapContextDrawerClass)){
1647
                        return false;
1648
                }
1649
                this.mapContextDrawerClass = mapContextDrawerClass;
1650
                if (this.mapContextDrawer != null){
1651
                        this.mapContextDrawer.dispose();
1652
                        this.mapContextDrawer = null;
1653
                }
1654
                return true;
1655

    
1656
        }
1657

    
1658
        public void setMapContextDrawer(MapContextDrawer drawer){
1659
                if (this.mapContextDrawer != null){
1660
                        this.mapContextDrawer.dispose();
1661
                        this.mapContextDrawer = null;
1662
                }
1663
                this.mapContextDrawer = drawer;
1664
                if (this.mapContextDrawer != null){
1665
                        this.mapContextDrawer.setMapContext(this);
1666
                        this.mapContextDrawer.setViewPort(viewPort);
1667
                }
1668
        }
1669

    
1670
        /**
1671
         * <p>Returns the OrderManager. When a new layer is added to this MapContext,
1672
         * the order manager will decide the right position for the
1673
         * layer.</p> <p>The orderManager will only be used when adding a layer,
1674
         * so it will not affect to the collection order when the user modifies the
1675
         * order.</p>
1676
         *
1677
         * @return The configured order manager, or DefaultOrderManager in no
1678
         * order manager was configured
1679
         */
1680
        public OrderManager getOrderManager() {
1681
                /**
1682
                 * Don't keep an order manager, always use a system-wide manager.
1683
                 *
1684
                 * In an idea scenario, we'd like to be able to configure an order
1685
                 * manager at different levels (system-wide, project-wide,
1686
                 * mapcontext-wide).
1687
                 *
1688
                 * However, gvSIG 1.9 does not provide the necessary infraestructure
1689
                 * to support this behaviour in a clean-way, so we don't offer this
1690
                 * behaviour at this time.
1691
                 */
1692
                return DefaultOrderManager.getDefaultOrderManager();
1693
        }
1694
}