Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.symbology / org.gvsig.symbology.lib / org.gvsig.symbology.lib.impl / src / main / java / org / gvsig / symbology / fmap / mapcontext / rendering / legend / impl / VectorialUniqueValueLegend.java @ 44985

History | View | Annotate | Download (16.9 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.symbology.fmap.mapcontext.rendering.legend.impl;
25

    
26
import java.awt.Color;
27
import java.util.ArrayList;
28
import java.util.Comparator;
29
import java.util.Iterator;
30
import java.util.List;
31
import java.util.Map;
32
import java.util.Map.Entry;
33
import java.util.Objects;
34
import java.util.TreeMap;
35
import java.util.logging.Level;
36
import org.apache.commons.lang3.ObjectUtils;
37
import org.apache.commons.lang3.mutable.MutableObject;
38
import org.gvsig.fmap.dal.DataTypeUtils;
39

    
40
import org.slf4j.Logger;
41
import org.slf4j.LoggerFactory;
42

    
43
import org.gvsig.fmap.dal.feature.Feature;
44
import org.gvsig.fmap.mapcontext.MapContextException;
45
import org.gvsig.fmap.mapcontext.MapContextLocator;
46
import org.gvsig.fmap.mapcontext.MapContextManager;
47
import org.gvsig.fmap.mapcontext.rendering.legend.IVectorialUniqueValueLegend;
48
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendClearEvent;
49
import org.gvsig.fmap.mapcontext.rendering.legend.events.LegendContentsChangedListener;
50
import org.gvsig.fmap.mapcontext.rendering.legend.events.SymbolLegendEvent;
51
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
52
import org.gvsig.tools.ToolsLocator;
53
import org.gvsig.tools.dataTypes.Coercion;
54
import org.gvsig.tools.dataTypes.CoercionException;
55
import org.gvsig.tools.dynobject.DynStruct;
56
import org.gvsig.tools.persistence.PersistenceManager;
57
import org.gvsig.tools.persistence.PersistentState;
58
import org.gvsig.tools.persistence.exception.PersistenceException;
59
import org.gvsig.tools.util.Callable;
60

    
61
/**
62
 * Vectorial legend for unique values
63
 *
64
 * @author Vicente Caballero Navarro
65
 * @author 2009- <a href="cordinyana@gvsig.org">C?sar Ordi?ana</a> - gvSIG team
66
 */
67
public class VectorialUniqueValueLegend extends AbstractClassifiedVectorLegend implements IVectorialUniqueValueLegend {
68
        final static private Logger LOG = LoggerFactory.getLogger(VectorialUniqueValueLegend.class);
69

    
70
        public static final String VECTORIAL_UNIQUE_VALUE_LEGEND_PERSISTENCE_DEFINITION_NAME =
71
                        "VectorialUniqueValueLegend";
72

    
73
        private static final String FIELD_KEYS = "keys";
74
        private static final String FIELD_NULL_VALUE_SYMBOL = "nullValueSymbol";
75
        private static final String FIELD_SELECTED_COLORS = "selectedColors";
76
        private static final String FIELD_SYMBOLS = "symbols";
77
        private static final String FIELD_USE_DEFAULT_SYMBOL = "useDefaultSymbol";
78

    
79
        private Map<Object, ISymbol> symbols = createSymbolMap();
80

    
81
        private List<Object> keys = new ArrayList<Object>();
82

    
83
    private ISymbol defaultSymbol;
84
    private int shapeType;
85
    private boolean useDefaultSymbol = false;
86
    private Color[] selectedColors=null;
87

    
88
    private ISymbol nullValueSymbol = null;
89

    
90
    public VectorialUniqueValueLegend() {
91
                super();
92
        }
93

    
94
    /**
95
     * Constructor method
96
     *
97
     * @param shapeType Type of the shape.
98
     */
99
    public VectorialUniqueValueLegend(int shapeType) {
100
                super();
101
            setShapeType(shapeType);
102
    }
103

    
104
        @Override
105
    public void setShapeType(int shapeType) {
106
            if (this.shapeType != shapeType) {
107
                    if(defaultSymbol==null || defaultSymbol.getSymbolType()!=shapeType){
108
                            ISymbol old = defaultSymbol;
109
                            defaultSymbol = getSymbolManager().createSymbol(shapeType);
110
                            fireDefaultSymbolChangedEvent(new SymbolLegendEvent(old, defaultSymbol));
111
                    }
112
                    this.shapeType = shapeType;
113
            }
114
    }
115

    
116
        @Override
117
    public void setValueSymbolByID(int id, ISymbol symbol) {
118
        ISymbol old = (ISymbol)symbols.put(keys.get(id), symbol);
119
        fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(old, symbol));
120
    }
121

    
122
        @Override
123
    public Object[] getValues() {
124
        return symbols.keySet().toArray(new Object[0]);
125
    }
126

    
127
        @Override
128
    public void addSymbol(Object key, ISymbol symbol) {
129
            ISymbol resul;
130
            if (key == null) {
131
                    resul = nullValueSymbol;
132
                    nullValueSymbol = symbol;
133
            } else {
134
                int[] dataTypes = this.getClassifyingFieldTypes();
135
                if(dataTypes==null){
136
                    throw new IllegalStateException("Classifying field types must be assigned before adding symbol");
137
                }
138
                try {
139
                    DataTypeUtils.coerce(dataTypes[0], key);
140
                } catch (CoercionException ex) {
141
                    throw new IllegalArgumentException(""+dataTypes[0]+" type key expected but received "+Objects.toString(key, "NULL"), ex);
142
                }
143
                        resul = (ISymbol) symbols.put(key, symbol);
144

    
145
                        if (resul != null) {
146
                                LOG.warn("Warning: the " + key + " key already existed. Resul = "
147
                                                + resul);
148
                                LOG.warn("new symbol :" + symbol.getDescription()
149
                                                + " old symbol = " + resul.getDescription());
150
                        } else {
151
                                keys.add(key);
152
                        }
153
            }
154

    
155
        fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(resul, symbol));
156
    }
157

    
158
    public void delAllSymbols() {
159
        keys.clear();
160
        ISymbol[] olds = (ISymbol[])symbols.values().toArray(new ISymbol[0]);
161
        symbols.clear();
162
        fireLegendClearEvent(new LegendClearEvent(olds));
163
    }
164
    
165
        @Override
166
    public void clear() {
167
        keys.clear();
168
        ISymbol[] olds = (ISymbol[])symbols.values().toArray(new ISymbol[0]);
169
        symbols.clear();
170
        removeLegendListener(getZSort());
171
        setZSort(null);
172

    
173
        fireLegendClearEvent(new LegendClearEvent(olds));
174
    }
175

    
176
        @Override
177
    public String[] getDescriptions() {
178
        String[] descriptions = new String[symbols.size()];
179
        ISymbol[] auxSym = getSymbols();
180

    
181
        for (int i = 0; i < descriptions.length; i++) {
182
                        descriptions[i] = auxSym[i].getDescription();
183
                }
184

    
185
        return descriptions;
186
    }
187

    
188
        @Override
189
     public ISymbol[] getSymbols() {
190
                 ISymbol[] symbolList;
191
                if (nullValueSymbol == null) {
192
                        symbolList = new ISymbol[symbols.size()];
193
                        return (ISymbol[]) symbols.values().toArray(symbolList);
194
                }
195
                else {
196
                        symbolList = new ISymbol[symbols.size() + 1];
197
                        symbolList[0] = nullValueSymbol;
198
                        int i = 1;
199
                        for (Iterator<ISymbol> iterator = symbols.values().iterator(); iterator
200
                                        .hasNext();) {
201
                                symbolList[i] = iterator.next();
202
                                i++;
203
                        }
204
                        return symbolList;
205
                }
206
    }
207

    
208
        @Override
209
    public void setClassifyingFieldNames(String[] fNames) {
210
        // TODO: Check if need more process
211
        super.setClassifyingFieldNames(fNames);
212
    }
213

    
214
    /**
215
         * Devuelve un s?mbolo a partir de una IFeature. OJO!! Cuando usamos un
216
         * feature iterator de base de datos el ?nico campo que vendr? rellenado es
217
         * el de fieldID. Los dem?s vendr?n a nulos para ahorra tiempo de creaci?n.
218
         *
219
         * @param feat
220
         *            IFeature
221
         *
222
         * @return S?mbolo.
223
     * @throws MapContextException
224
         */
225
        @Override
226
    public ISymbol getSymbolByFeature(Feature feat) throws MapContextException {
227

    
228
        Object val = null;
229
        String classifyingFieldName = getClassifyingFieldNames()[0];
230
        try {
231
            val = feat.get(classifyingFieldName);
232
        } catch (Exception e) {
233
            LOG.info("Can't get value from field \"" + classifyingFieldName + "\". Return null or defaultSymbol", e);
234
        }
235

    
236
        ISymbol theSymbol = getSymbolByValue(val);
237

    
238
        if (theSymbol != null) {
239
                return theSymbol;
240
        }
241

    
242
        if (isUseDefaultSymbol()) {
243
                        return defaultSymbol;
244
                }
245

    
246
        return null;
247
    }
248

    
249

    
250
        @Override
251
    public ISymbol getDefaultSymbol() {
252

    
253
            if(defaultSymbol==null) {
254
                        defaultSymbol = getSymbolManager().createSymbol(shapeType);
255
                    fireDefaultSymbolChangedEvent(new SymbolLegendEvent(null, defaultSymbol));
256
            }
257
            return defaultSymbol;
258
    }
259

    
260
        @Override
261
        public void setDefaultSymbol(ISymbol s) {
262
            ISymbol mySymbol = defaultSymbol;
263

    
264
            if (s == null) {
265
                        throw new NullPointerException("Default symbol cannot be null");
266
                }
267

    
268
            ISymbol old = mySymbol;
269
            defaultSymbol = s;
270
            fireDefaultSymbolChangedEvent(new SymbolLegendEvent(old, s));
271
    }
272

    
273

    
274
    /*
275
     * (non-Javadoc)
276
     *
277
     * @see com.iver.cit.gvsig.fmap.rendering.UniqueValueLegend#getSymbolByValue(com.hardcode.gdbms.engine.values.Value)
278
     */
279
        @Override
280
    public ISymbol getSymbolByValue(Object key) {
281
            ISymbol symbol = null;
282
            if (key == null) {
283
                    symbol = nullValueSymbol;
284
            }
285
            else {
286
                    symbol = (ISymbol)symbols.get(key);
287
            }
288
            if (symbol == null && useDefaultSymbol) {
289
                    symbol = getDefaultSymbol();
290
            }
291
            return symbol;
292

    
293
    }
294

    
295
        @Override
296
        public Object getSymbolKey(ISymbol symbol) {
297
                if (symbol != null) {
298
                        for (Iterator<Entry<Object, ISymbol>> iterator = symbols.entrySet()
299
                                        .iterator(); iterator.hasNext();) {
300
                                Entry<Object, ISymbol> entry = iterator.next();
301
                                if (symbol.equals(entry.getValue())) {
302
                                        return entry.getKey();
303
                                }
304
                        }
305
                }
306
                return null;
307
        }
308

    
309
        @Override
310
    public int getShapeType() {
311
        return shapeType;
312
    }
313

    
314
        @Override
315
    public void useDefaultSymbol(boolean b) {
316
        useDefaultSymbol = b;
317
    }
318

    
319
    /**
320
         * Devuelve si se utiliza o no el resto de valores para representarse.
321
         * @return  True si se utiliza el resto de valores.
322
         */
323
        @Override
324
    public boolean isUseDefaultSymbol() {
325
        return useDefaultSymbol;
326
    }
327

    
328
        @Override
329
    public void delSymbol(Object key) {
330
        keys.remove(key);
331

    
332
                ISymbol removedSymbol = (ISymbol) symbols.remove(key);
333
                if (removedSymbol != null){
334
                        fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(removedSymbol, null));
335
                }
336
    }
337

    
338
        public String getClassName() {
339
                return getClass().getName();
340
        }
341

    
342
        @Override
343
        public void replace(ISymbol oldSymbol, ISymbol newSymbol) {
344
                if (symbols.containsValue(oldSymbol)) {
345
                        Iterator<Object> it = symbols.keySet().iterator();
346
                        while (it.hasNext()) {
347
                                Object key = it.next();
348
                                if (symbols.get(key).equals(oldSymbol)) {
349
                                        fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(
350
                                                        (ISymbol)symbols.put(key, newSymbol), newSymbol));
351
                                }
352

    
353
                        }
354
                }
355
        }
356
        @Override
357
        public Color[] getColorScheme() {
358
                return selectedColors;
359
        }
360

    
361
        @Override
362
        public void setColorScheme(Color[] cc) {
363
                 this.selectedColors = cc;
364
        }
365

    
366
        @Override
367
        public Object clone() throws CloneNotSupportedException {
368
                VectorialUniqueValueLegend clone =
369
                                (VectorialUniqueValueLegend) super.clone();
370

    
371
                // Clone default symbol
372
                if (defaultSymbol != null) {
373
                        clone.defaultSymbol = (ISymbol) defaultSymbol.clone();
374
                }
375
                // Clone keys
376
                clone.keys = new ArrayList<Object>();
377

    
378
        // ====================================================
379
                // Temporarily remove listeners to prevent
380
                // cascade of notifications
381
                LegendContentsChangedListener[] list = this.getListeners();
382
                removeListeners(list);
383
        // ====================================================
384

    
385
                // Clone symbols
386
                if (symbols != null) {
387
                        clone.symbols = createSymbolMap();
388
                        for (Iterator<Entry<Object, ISymbol>> iterator =
389
                                        symbols.entrySet().iterator(); iterator.hasNext();) {
390
                                Entry<Object, ISymbol> entry = iterator.next();
391
                                ISymbol symbolClone =
392
                                                (ISymbol) ((ISymbol) entry.getValue()).clone();
393
                                // Map keys are of type Number or String, so being
394
                                // immutable objects, don't clone them
395
                                clone.addSymbol(entry.getKey(), symbolClone);
396
                        }
397
                }
398

    
399
        // ====================================================
400
        // Restore listeners
401
        addListeners(list);
402
        // ====================================================
403
                return clone;
404
        }
405

    
406

    
407
    private void addListeners(LegendContentsChangedListener[] list) {
408
        int len = list.length;
409
        for (int i=0; i<len; i++) {
410
            addLegendListener(list[i]);
411
        }
412
    }
413

    
414
    private void removeListeners(LegendContentsChangedListener[] list) {
415
        int len = list.length;
416
        for (int i=0; i<len; i++) {
417
            removeLegendListener(list[i]);
418
        }
419
    }
420
    
421
    public void setClassifyingFieldTypes(int[] fieldTypes) {
422
        
423
        Class fieldClass = ToolsLocator.getDataTypesManager().getDefaultClass(fieldTypes[0]);
424
        if (!Comparable.class.isAssignableFrom(fieldClass)) {
425
             throw new IllegalArgumentException("Should be comparable");   
426
        }
427
        super.setClassifyingFieldTypes(fieldTypes);
428
    }
429
        
430
    private Map<Object, ISymbol> createSymbolMap() {
431
        final MutableObject<Coercion> convert = new MutableObject<>();
432

    
433
        return new TreeMap<Object, ISymbol>(
434
                new Comparator<Object>() {
435
            public int compare(Object o1, Object o2) {
436
                if (convert.getValue() == null) {
437
                int [] classifyingFieldTypes = getClassifyingFieldTypes();
438
                    if(classifyingFieldTypes == null){
439
                        throw new RuntimeException("Symbols are being added to the legend before assigning classifying fields");
440
                    }
441
                    convert.setValue(ToolsLocator.getDataTypesManager().getCoercion(classifyingFieldTypes[0]));
442
                }
443
                try {
444
                    return ObjectUtils.compare((Comparable) (convert.getValue().coerce(o1)), (Comparable) (convert.getValue().coerce(o2)));
445
                } catch (CoercionException ex) {
446
                    throw new RuntimeException("Not able to coerce values in symbol map");
447
                }
448
            }
449
        });
450
    }
451

    
452
        @SuppressWarnings({ "unchecked", "rawtypes" })
453
        public void loadFromState(PersistentState state)
454
                        throws PersistenceException {
455
                // Set parent properties
456
                super.loadFromState(state);
457
                // Set own properties
458
                keys = new ArrayList<Object>((List) state.get(FIELD_KEYS));
459
                nullValueSymbol = (ISymbol) state.get(FIELD_NULL_VALUE_SYMBOL);
460
                selectedColors =
461
                                (Color[]) state.getArray(FIELD_SELECTED_COLORS, Color.class);
462
                Map persistedSymbolMap = (Map) state.get(FIELD_SYMBOLS);
463
                if (persistedSymbolMap != null) {
464
                        symbols.putAll(persistedSymbolMap);
465
                }
466
                useDefaultSymbol(state.getBoolean(FIELD_USE_DEFAULT_SYMBOL));
467
        }
468

    
469
        public void saveToState(PersistentState state) throws PersistenceException {
470
                // Save parent properties
471
                super.saveToState(state);
472
                // Save own properties
473
                state.set(FIELD_KEYS, keys);
474
                state.set(FIELD_NULL_VALUE_SYMBOL, nullValueSymbol);
475
                state.set(FIELD_SELECTED_COLORS, selectedColors);
476
                state.set(FIELD_USE_DEFAULT_SYMBOL, isUseDefaultSymbol());
477
                state.set(FIELD_SYMBOLS, symbols);
478
        }
479

    
480
        public static class RegisterPersistence implements Callable {
481

    
482
                public Object call() throws Exception {
483
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
484
                        if( manager.getDefinition(VECTORIAL_UNIQUE_VALUE_LEGEND_PERSISTENCE_DEFINITION_NAME)==null ) {
485
                                DynStruct definition = manager.addDefinition(
486
                                                VectorialUniqueValueLegend.class,
487
                                                VECTORIAL_UNIQUE_VALUE_LEGEND_PERSISTENCE_DEFINITION_NAME,
488
                                                VECTORIAL_UNIQUE_VALUE_LEGEND_PERSISTENCE_DEFINITION_NAME+" Persistence definition",
489
                                                null,
490
                                                null
491
                                );
492
                                // Extend the Classified Vector Legend base definition
493
                                definition.extend(manager.getDefinition(CLASSIFIED_VECTOR_LEGEND_PERSISTENCE_DEFINITION_NAME));
494

    
495
                                // Keys
496
                                definition.addDynFieldList(FIELD_KEYS).setClassOfItems(Object.class);
497
                                // Null interval symbol
498
                                definition.addDynFieldObject(FIELD_NULL_VALUE_SYMBOL).setClassOfValue(ISymbol.class);
499
                                // Selected colors
500
                                definition.addDynFieldList(FIELD_SELECTED_COLORS).setClassOfItems(Color.class);
501
                                // Symbols
502
                                definition.addDynFieldMap(FIELD_SYMBOLS).setClassOfItems(ISymbol.class);
503
                                // Use default symbol?
504
                                definition.addDynFieldBoolean(FIELD_USE_DEFAULT_SYMBOL);
505
                        }
506
                        return Boolean.TRUE;
507
                }
508

    
509
        }
510

    
511
        public static class RegisterLegend implements Callable {
512

    
513
                public Object call() throws Exception {
514
                MapContextManager manager = MapContextLocator.getMapContextManager();
515

    
516
            manager.registerLegend(IVectorialUniqueValueLegend.LEGEND_NAME,
517
                    VectorialUniqueValueLegend.class);
518

    
519
                        return Boolean.TRUE;
520
                }
521

    
522
        }
523
}