Statistics
| Revision:

root / org.gvsig.legend.vectorfilterexpression.app.mainplugin / trunk / org.gvsig.legend.vectorfilterexpression.app.mainplugin / src / main / java / org / gvsig / symbology / fmap / rendering / VectorFilterExpressionLegend.java @ 1856

History | View | Annotate | Download (16.1 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 org.gvsig.symbology.fmap.rendering;
42

    
43

    
44
import java.util.ArrayList;
45

    
46
import org.slf4j.Logger;
47
import org.slf4j.LoggerFactory;
48

    
49
import org.gvsig.fmap.dal.feature.Feature;
50
import org.gvsig.fmap.geom.Geometry;
51
import org.gvsig.fmap.geom.GeometryLocator;
52
import org.gvsig.fmap.geom.GeometryManager;
53
import org.gvsig.fmap.mapcontext.MapContextLocator;
54
import org.gvsig.fmap.mapcontext.MapContextManager;
55
import org.gvsig.fmap.mapcontext.rendering.legend.events.SymbolLegendEvent;
56
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
57
import org.gvsig.i18n.Messages;
58
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.impl.AbstractClassifiedVectorLegend;
59
import org.gvsig.symbology.fmap.rendering.filterexpression.EvaluatorCreator;
60
import org.gvsig.tools.ToolsLocator;
61
import org.gvsig.tools.dynobject.DynStruct;
62
import org.gvsig.tools.evaluator.Evaluator;
63
import org.gvsig.tools.evaluator.EvaluatorData;
64
import org.gvsig.tools.evaluator.EvaluatorWithDescriptions;
65
import org.gvsig.tools.persistence.PersistenceManager;
66
import org.gvsig.tools.persistence.PersistentState;
67
import org.gvsig.tools.persistence.exception.PersistenceException;
68
import org.gvsig.tools.util.Callable;
69

    
70

    
71
/**
72
 *
73
 * Implements a vector legend which represents the elements of a layer
74
 * depending on the value of an expression. That is, if the expression is
75
 * evaluated to true, then the symbol associated to the expression is painted.
76
 * In other case it is not showed.
77
 *
78
 * If the expression result is a string, it is considered false if
79
 * it is the empty string "", true otherwise.
80
 *
81
 * If the expression result is numeric, it is considered false
82
 * if it is zero, and true otherwise.
83
 *
84
 * In other cases, it is considered false if the result is null and true
85
 * otherwise.
86
 *
87
 * @author Pepe Vidal Salvador - jose.vidal.salvador@iver.es
88
 * @author jldominguez
89
 */
90
public class VectorFilterExpressionLegend
91
extends AbstractClassifiedVectorLegend  {
92

    
93
    private static final Logger logger = LoggerFactory.getLogger(
94
        VectorFilterExpressionLegend.class);
95

    
96
    public static final String
97
    FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME =
98
    "FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME";
99

    
100
    public static final String
101
    FILTER_EXPRESSION_LEGEND_NAME = "FILTER_EXPRESSION_LEGEND_NAME";
102

    
103
    public static final String I18N_DEFAULT = Messages.getText("default_value");
104
    public static final String NON_I18N_DEFAULT = Messages.getText("Default");
105

    
106
        private int shapeType;
107
        private ISymbol defaultSymbol;
108

    
109
        private boolean useDefaultSymbol = false;
110

    
111
        private long error_msg_count = 0;
112

    
113
        private ArrayList<Item> newSymbols = new ArrayList<Item>() {
114
                private static final long serialVersionUID = 1L;
115

    
116
                public int indexOf(String expr) {
117
                        return super.indexOf(new Item(expr, null));
118
                }
119
        };
120

    
121

    
122
        private class Item implements Cloneable {
123

    
124
                private ISymbol sym;
125
                // get it with getSql()
126
                // private String expression;
127
                private EvaluatorWithDescriptions evaluator;
128

    
129
                public Item(String expression, ISymbol sym) {
130
                        this.sym = sym;
131
                        evaluator = EvaluatorCreator.getEvaluator(expression);
132
                }
133

    
134
                public boolean equals(Object obj) {
135
                        if (obj == null) return false;
136
                        if (!obj.getClass().equals(Item.class)) return false;
137
                        return this.evaluator.getSQL().equals(
138
                            ((Item) obj).evaluator.getSQL()
139
                            );
140
                }
141

    
142
                public String getStringExpression() {
143
                        return evaluator.getSQL();
144
                }
145

    
146
                public Evaluator getEvaluator() {
147
                        return evaluator;
148
                }
149

    
150
                public Object clone() {
151

    
152
                    ISymbol clonesym = null;
153
                    try {
154
                clonesym = (ISymbol) this.sym.clone();
155
            } catch (CloneNotSupportedException e) {
156
                logger.info("Error: unable to clone symbol.", e);
157
                clonesym = this.sym;
158
            }
159
                    return new Item(getStringExpression(), clonesym);
160
                }
161
        }
162

    
163
    public VectorFilterExpressionLegend() {
164
        this.setClassifyingFieldNames(new String[0]);
165
        this.setClassifyingFieldTypes(new int[0]);
166
    }
167

    
168
        public ISymbol getSymbolByFeature(Feature featu) {
169

    
170
            EvaluatorData evda = featu.getEvaluatorData();
171

    
172
                ISymbol returnSymbol = null;
173
                Object result = null;
174
                String expr = null;
175

    
176
                try {
177

    
178
                        for (int i = 0; i < newSymbols.size(); i++) {
179

    
180
                                Evaluator eval = newSymbols.get(i).getEvaluator();
181
                                expr = eval.getSQL();
182

    
183
                if (expr.equalsIgnoreCase(VectorFilterExpressionLegend.I18N_DEFAULT)){
184
                    /*
185
                     * Skip default item
186
                     */
187
                    continue;
188
                }
189

    
190
                                result = eval.evaluate(evda);
191
                                if (isConsideredTrue(result)) {
192
                    returnSymbol = newSymbols.get(i).sym;
193
                    if (returnSymbol != null) {
194
                        return returnSymbol;
195
                    }
196
                                }
197
                        }
198
                } catch (Exception e) {
199

    
200
                    if (error_msg_count % 1000 == 0) {
201
                        logger.info("Error (msg every 1000 occurrences) while getting symbol in VectorFilterExpressionLegend", e);
202
                        error_msg_count = 0;
203
                    }
204
                    error_msg_count++;
205
                }
206

    
207
                if (useDefaultSymbol)
208
                        return getDefaultSymbol();
209

    
210
                return null;
211
        }
212

    
213
        /**
214
         * Tells whether the input object is considered true.
215
         * Basically, it is false if it has an empty value
216
         * (FALSE, null, 0, "")
217
         *
218
     * @param result
219
     * @return
220
     */
221
    private boolean isConsideredTrue(Object res) {
222

    
223
        if (res == null) {
224
            return false;
225
        }
226

    
227
        if (res instanceof Boolean) {
228
            return ((Boolean) res).booleanValue();
229
        }
230

    
231
        if (res instanceof Number) {
232
            return ((Number) res).doubleValue() != 0d;
233
        }
234

    
235
        if (res instanceof String) {
236
            return ((String) res).length() > 0;
237
        }
238

    
239
        // Because it is not null
240
        return true;
241
    }
242

    
243
        public void addSymbol(Object key, ISymbol symbol) {
244
                newSymbols.add(new Item((String)key.toString(),
245
                                symbol));
246
        }
247

    
248
        public void clear() {
249
                newSymbols.clear();
250
        }
251

    
252
        public void resetItems() {
253
            newSymbols = new ArrayList<Item>() {
254
            private static final long serialVersionUID = 1L;
255

    
256
            public int indexOf(String expr) {
257
                return super.indexOf(new Item(expr, null));
258
            }
259
            };
260
        }
261

    
262
        public void delSymbol(Object key) {
263
                ISymbol mySymbol = null;
264
                for (int i = 0; i < newSymbols.size(); i++) {
265
                        if (newSymbols.get(i).evaluator.getSQL().equals(key))
266
                                newSymbols.remove(i);
267
                }
268
                fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(mySymbol,null));
269
        }
270

    
271

    
272
        public void replace(ISymbol oldSymbol, ISymbol newSymbol) {
273

    
274
                for (int i = 0; i < newSymbols.size(); i++) {
275
                        if (newSymbols.get(i).sym.equals(oldSymbol))
276
                                newSymbols.get(i).sym = newSymbol;
277
                }
278

    
279
                fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(oldSymbol,newSymbol));
280
        }
281

    
282

    
283
        public String[] getDescriptions() {
284
                String[] descriptions = new String[newSymbols.size()];
285
                ISymbol[] auxSym = getSymbols();
286

    
287
                for (int i = 0; i < descriptions.length; i++)
288
                        descriptions[i] = auxSym[i].getDescription();
289

    
290
                return descriptions;
291
        }
292

    
293
        public ISymbol[] getSymbols() {
294

    
295
                if (newSymbols != null) {
296
                        ISymbol[] mySymbols = new ISymbol[newSymbols.size()];
297
                        for (int i = 0; i < newSymbols.size(); i++) {
298
                                mySymbols[i] = newSymbols.get(i).sym;
299
                        }
300
                        return mySymbols;
301
                }
302
                return null;
303
        }
304

    
305

    
306

    
307
        public ISymbol getDefaultSymbol() {
308
                if(defaultSymbol==null) {
309

    
310
                    defaultSymbol = MapContextLocator.getSymbolManager(
311
                        ).createSymbol(shapeType);
312
                        fireDefaultSymbolChangedEvent(new SymbolLegendEvent(null, defaultSymbol));
313
                }
314
                return defaultSymbol;
315
        }
316

    
317

    
318
        public String getClassName() {
319
                return getClass().getName();
320
        }
321

    
322

    
323
        public int getShapeType() {
324
                return shapeType;
325
        }
326

    
327
        public boolean isUseDefaultSymbol() {
328
                return useDefaultSymbol;
329
        }
330

    
331
        public void setDefaultSymbol(ISymbol s) throws IllegalArgumentException {
332
                if (s == null) throw new NullPointerException("Default symbol cannot be null");
333
                ISymbol old = defaultSymbol;
334
                defaultSymbol = s;
335
                fireDefaultSymbolChangedEvent(new SymbolLegendEvent(old, defaultSymbol));
336
        }
337

    
338
        public void setShapeType(int shapeType) {
339
                if (this.shapeType != shapeType) {
340

    
341
                    ISymbol sym = MapContextLocator.getSymbolManager(
342
                        ).createSymbol(shapeType);
343
                        setDefaultSymbol(sym);
344
                        this.shapeType = shapeType;
345
                }
346
        }
347

    
348
        public void useDefaultSymbol(boolean b) {
349
                useDefaultSymbol = b;
350
        }
351

    
352
        public Object[] getValues() {
353
                if (newSymbols != null) {
354
                        Object[] myObjects = new Object[newSymbols.size()];
355
                        for (int i = 0; i < newSymbols.size(); i++) {
356
                                myObjects[i] = newSymbols.get(i).getStringExpression();
357
                        }
358
                        return myObjects;
359
                }
360
                return null;
361
        }
362

    
363
    public static boolean isPolygonal(int ty) {
364
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
365
        return geomManager.isSubtype(Geometry.TYPES.MULTISURFACE, ty) || 
366
            geomManager.isSubtype(Geometry.TYPES.SURFACE, ty);
367
    }
368

    
369

    
370
    public static boolean isLinear(int ty) {
371
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
372
        return geomManager.isSubtype(Geometry.TYPES.MULTICURVE, ty) || 
373
            geomManager.isSubtype(Geometry.TYPES.CURVE, ty);
374
    }
375
    
376
    public static boolean isPoint(int ty) {
377
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
378
        return geomManager.isSubtype(Geometry.TYPES.MULTIPOINT, ty) || 
379
            geomManager.isSubtype(Geometry.TYPES.POINT, ty);
380
    }
381
    
382
    public void removeDefaultSymbol() {
383

    
384
    }
385

    
386
    // =============================
387

    
388
    public static class RegisterPersistence implements Callable {
389

    
390
        public Object call() throws Exception {
391

    
392
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
393
            if (manager.getDefinition(
394
                FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME) == null) {
395
                DynStruct definition = manager
396
                    .addDefinition(VectorFilterExpressionLegend.class,
397
                        FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME,
398
                        FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME
399
                        + " Persistence definition", null, null);
400

    
401
                definition.extend(manager.getDefinition(
402
                    AbstractClassifiedVectorLegend
403
                    .CLASSIFIED_VECTOR_LEGEND_PERSISTENCE_DEFINITION_NAME));
404

    
405
                definition.addDynFieldBoolean("useDefaultSymbol")
406
                .setMandatory(true);
407
                definition.addDynFieldObject("defaultSymbol")
408
                .setClassOfValue(ISymbol.class).setMandatory(true);
409
                definition.addDynFieldInt("shapeType")
410
                .setMandatory(true);
411

    
412
                definition.addDynFieldArray("itemSymbolArray")
413
                .setClassOfItems(ISymbol.class);
414
                definition.addDynFieldArray("itemStringArray")
415
                .setClassOfItems(String.class);
416

    
417
            }
418
            return Boolean.TRUE;
419
        }
420

    
421
    }
422

    
423
    public static class RegisterLegend implements Callable {
424

    
425
        public Object call() throws Exception {
426
            MapContextManager manager =
427
                MapContextLocator.getMapContextManager();
428

    
429
            manager.registerLegend(
430
                FILTER_EXPRESSION_LEGEND_NAME,
431
                VectorFilterExpressionLegend.class);
432

    
433
            return Boolean.TRUE;
434
        }
435

    
436
    }
437

    
438
    public void saveToState(PersistentState state) throws PersistenceException {
439

    
440
        super.saveToState(state);
441
        state.set("useDefaultSymbol", this.isUseDefaultSymbol());
442
        state.set("defaultSymbol", this.getDefaultSymbol());
443
        state.set("shapeType", this.getShapeType());
444

    
445
        ISymbol[] syms = this.getSymbols();
446
        if (syms == null) {
447
            syms = new ISymbol[0];
448
        }
449
        Object[] vals = this.getValues();
450
        String[] vals_str = null;
451
        if (vals == null) {
452
            vals_str = new String[0];
453
        } else {
454
            vals_str = new String[vals.length];
455
            for (int i=0; i<vals.length; i++) {
456
                String aux = ((vals[i] == null) ? null : vals[i].toString());
457
                // Prevents saving localized version of 'Default'
458
                aux = translateDefault(aux, false);
459
                vals_str[i] = aux;
460
            }
461
        }
462

    
463
        state.set("itemSymbolArray", syms);
464
        state.set("itemStringArray", vals_str);
465

    
466
    }
467

    
468
    public void loadFromState(PersistentState state)
469
        throws PersistenceException {
470

    
471
        super.loadFromState(state);
472

    
473
        this.setShapeType(state.getInt("shapeType"));
474
        Boolean b = state.getBoolean("useDefaultSymbol");
475
        this.useDefaultSymbol(b);
476
        ISymbol defsym = (ISymbol) state.get("defaultSymbol");
477
        this.setDefaultSymbol(defsym);
478

    
479
        String[] strs = state.getStringArray("itemStringArray");
480
        ISymbol[] syms = (ISymbol[]) state.getArray("itemSymbolArray",
481
            ISymbol.class);
482

    
483
        if (strs.length != syms.length) {
484
            logger.info("VectorFilterExpressionLegend - load state - Different size in arrays: " + strs.length + ", " + syms.length);
485
        }
486
        int nmin = Math.min(strs.length, syms.length);
487
        for (int i=0; i<nmin; i++) {
488
            String aux = strs[i];
489
            aux = translateDefault(aux, true);
490
            this.addSymbol(aux, syms[i]);
491
        }
492
    }
493

    
494
    /**
495
     * Utility method to (un)translate the word 'Default'
496
     * @param aux
497
     * @param forward
498
     * If TRUE, then translate (Default -> Por defecto)
499
     * If FALSE then untranslate (Por defecto -> Default)
500
     * @return
501
     */
502
    private String translateDefault(String aux, boolean forward) {
503

    
504
        if (aux == null) {
505
            return null;
506
        }
507
        if (forward && aux.compareTo(NON_I18N_DEFAULT) == 0) {
508
            return I18N_DEFAULT;
509
        }
510
        if (!forward && aux.compareTo(I18N_DEFAULT) == 0) {
511
            return NON_I18N_DEFAULT;
512
        }
513
        return aux;
514
    }
515

    
516

    
517
    public Object clone() throws CloneNotSupportedException {
518

    
519
        VectorFilterExpressionLegend resp =
520
            (VectorFilterExpressionLegend) super.clone();
521

    
522
        Object[] vals = this.getValues();
523
        ISymbol[] syms = this.getSymbols();
524
        if (vals != null && syms != null) {
525

    
526
            resp.resetItems();
527

    
528
            int n = Math.min(vals.length, syms.length);
529
            for (int i=0; i<n; i++) {
530
                resp.addSymbol(
531
                    vals[i],
532
                    (ISymbol) syms[i].clone());
533
            }
534
        }
535
        ISymbol sym = this.getDefaultSymbol();
536
        sym = (ISymbol) sym.clone();
537
        resp.setDefaultSymbol(sym);
538
        return resp;
539
    }
540

    
541

    
542

    
543
}