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 @ 3450

History | View | Annotate | Download (16.7 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2020 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.rendering;
25

    
26

    
27
import java.util.ArrayList;
28
import org.gvsig.fmap.dal.DALLocator;
29
import org.gvsig.fmap.dal.DataManager;
30
import org.gvsig.fmap.dal.exception.InitializeException;
31

    
32
import org.slf4j.Logger;
33
import org.slf4j.LoggerFactory;
34

    
35
import org.gvsig.fmap.dal.feature.Feature;
36
import org.gvsig.fmap.geom.Geometry;
37
import org.gvsig.fmap.geom.GeometryLocator;
38
import org.gvsig.fmap.geom.GeometryManager;
39
import org.gvsig.fmap.mapcontext.MapContextLocator;
40
import org.gvsig.fmap.mapcontext.MapContextManager;
41
import org.gvsig.fmap.mapcontext.rendering.legend.events.SymbolLegendEvent;
42
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
43
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
44
import org.gvsig.i18n.Messages;
45
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.driver.impl.PersistenceBasedLegendWriter;
46
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.impl.AbstractClassifiedVectorLegend;
47
import org.gvsig.tools.ToolsLocator;
48
import org.gvsig.tools.dynobject.DynStruct;
49
import org.gvsig.tools.evaluator.Evaluator;
50
import org.gvsig.tools.evaluator.EvaluatorData;
51
import org.gvsig.tools.persistence.PersistenceManager;
52
import org.gvsig.tools.persistence.PersistentState;
53
import org.gvsig.tools.persistence.exception.PersistenceException;
54
import org.gvsig.tools.util.Callable;
55
import org.gvsig.tools.util.Caller;
56
import org.gvsig.tools.util.impl.DefaultCaller;
57

    
58

    
59
/**
60
 *
61
 * Implements a vector legend which represents the elements of a layer
62
 * depending on the value of an expression. That is, if the expression is
63
 * evaluated to true, then the symbol associated to the expression is painted.
64
 * In other case it is not showed.
65
 *
66
 * If the expression result is a string, it is considered false if
67
 * it is the empty string "", true otherwise.
68
 *
69
 * If the expression result is numeric, it is considered false
70
 * if it is zero, and true otherwise.
71
 *
72
 * In other cases, it is considered false if the result is null and true
73
 * otherwise.
74
 *
75
 * @author gvSIG Team
76
 */
77
public class VectorFilterExpressionLegend
78
extends AbstractClassifiedVectorLegend  {
79

    
80
    private static final Logger logger = LoggerFactory.getLogger(
81
        VectorFilterExpressionLegend.class);
82

    
83
    public static final String
84
    FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME =
85
    "FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME";
86

    
87
    public static final String
88
    FILTER_EXPRESSION_LEGEND_NAME = "FILTER_EXPRESSION_LEGEND_NAME";
89

    
90
    public static final String I18N_DEFAULT = Messages.getText("default_value");
91
    public static final String NON_I18N_DEFAULT = Messages.getText("Default");
92

    
93
        private int shapeType;
94
        private ISymbol defaultSymbol;
95

    
96
        private boolean useDefaultSymbol = false;
97

    
98
        private long error_msg_count = 0;
99

    
100
        private ArrayList<Item> newSymbols = new ArrayList<Item>() {
101
                private static final long serialVersionUID = 1L;
102

    
103
                public int indexOf(String expr) {
104
                        return super.indexOf(new Item(expr, null));
105
                }
106
        };
107

    
108

    
109
        private class Item implements Cloneable {
110

    
111
                private ISymbol sym;
112
                private Evaluator evaluator;
113

    
114
                public Item(String expression, ISymbol sym) {
115
                        this.sym = sym;
116
      DataManager dataManager = DALLocator.getDataManager();
117
      try {
118
        evaluator = dataManager.createFilter(expression);
119
      } catch (InitializeException ex) {
120
        evaluator = null;
121
      }
122
                }
123

    
124
                public boolean equals(Object obj) {
125
                        if (obj == null) return false;
126
                        if (!obj.getClass().equals(Item.class)) return false;
127
                        return this.evaluator.getSQL().equals(
128
                            ((Item) obj).evaluator.getSQL()
129
                            );
130
                }
131

    
132
                public String getStringExpression() {
133
                        return evaluator.getSQL();
134
                }
135

    
136
                public Evaluator getEvaluator() {
137
                        return evaluator;
138
                }
139

    
140
                public Object clone() {
141

    
142
                    ISymbol clonesym = null;
143
                    try {
144
                clonesym = (ISymbol) this.sym.clone();
145
            } catch (CloneNotSupportedException e) {
146
                logger.info("Error: unable to clone symbol.", e);
147
                clonesym = this.sym;
148
            }
149
                    return new Item(getStringExpression(), clonesym);
150
                }
151
        }
152

    
153
    public VectorFilterExpressionLegend() {
154
        this.setClassifyingFieldNames(new String[0]);
155
        this.setClassifyingFieldTypes(new int[0]);
156
    }
157

    
158
        public ISymbol getSymbolByFeature(Feature featu) {
159

    
160
            EvaluatorData evda = featu.getEvaluatorData();
161

    
162
                ISymbol returnSymbol = null;
163
                Object result = null;
164
                String expr = null;
165

    
166
                try {
167

    
168
                        for (int i = 0; i < newSymbols.size(); i++) {
169

    
170
                                Evaluator eval = newSymbols.get(i).getEvaluator();
171
                                expr = eval.getSQL();
172

    
173
                if (expr.equalsIgnoreCase(VectorFilterExpressionLegend.I18N_DEFAULT)){
174
                    /*
175
                     * Skip default item
176
                     */
177
                    continue;
178
                }
179

    
180
                                result = eval.evaluate(evda);
181
                                if (isConsideredTrue(result)) {
182
                    returnSymbol = newSymbols.get(i).sym;
183
                    if (returnSymbol != null) {
184
                        return returnSymbol;
185
                    }
186
                                }
187
                        }
188
                } catch (Exception e) {
189

    
190
                    if (error_msg_count % 1000 == 0) {
191
                        logger.info("Error (msg every 1000 occurrences) while getting symbol in VectorFilterExpressionLegend", e);
192
                        error_msg_count = 0;
193
                    }
194
                    error_msg_count++;
195
                }
196

    
197
                if (useDefaultSymbol)
198
                        return getDefaultSymbol();
199

    
200
                return null;
201
        }
202

    
203
        /**
204
         * Tells whether the input object is considered true.
205
         * Basically, it is false if it has an empty value
206
         * (FALSE, null, 0, "")
207
         *
208
     * @param result
209
     * @return
210
     */
211
    private boolean isConsideredTrue(Object res) {
212

    
213
        if (res == null) {
214
            return false;
215
        }
216

    
217
        if (res instanceof Boolean) {
218
            return ((Boolean) res).booleanValue();
219
        }
220

    
221
        if (res instanceof Number) {
222
            return ((Number) res).doubleValue() != 0d;
223
        }
224

    
225
        if (res instanceof String) {
226
            return ((String) res).length() > 0;
227
        }
228

    
229
        // Because it is not null
230
        return true;
231
    }
232

    
233
        public void addSymbol(Object key, ISymbol symbol) {
234
                newSymbols.add(new Item((String)key.toString(),
235
                                symbol));
236
        }
237

    
238
        public void clear() {
239
                newSymbols.clear();
240
        }
241

    
242
        public void resetItems() {
243
            newSymbols = new ArrayList<Item>() {
244
            private static final long serialVersionUID = 1L;
245

    
246
            public int indexOf(String expr) {
247
                return super.indexOf(new Item(expr, null));
248
            }
249
            };
250
        }
251

    
252
        public void delSymbol(Object key) {
253
                ISymbol mySymbol = null;
254
                for (int i = 0; i < newSymbols.size(); i++) {
255
                        if (newSymbols.get(i).evaluator.getSQL().equals(key))
256
                                newSymbols.remove(i);
257
                }
258
                fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(mySymbol,null));
259
        }
260

    
261

    
262
        public void replace(ISymbol oldSymbol, ISymbol newSymbol) {
263

    
264
                for (int i = 0; i < newSymbols.size(); i++) {
265
                        if (newSymbols.get(i).sym.equals(oldSymbol))
266
                                newSymbols.get(i).sym = newSymbol;
267
                }
268

    
269
                fireClassifiedSymbolChangeEvent(new SymbolLegendEvent(oldSymbol,newSymbol));
270
        }
271

    
272

    
273
        public String[] getDescriptions() {
274
                String[] descriptions = new String[newSymbols.size()];
275
                ISymbol[] auxSym = getSymbols();
276

    
277
                for (int i = 0; i < descriptions.length; i++)
278
                        descriptions[i] = auxSym[i].getDescription();
279

    
280
                return descriptions;
281
        }
282

    
283
        public ISymbol[] getSymbols() {
284

    
285
                if (newSymbols != null) {
286
                        ISymbol[] mySymbols = new ISymbol[newSymbols.size()];
287
                        for (int i = 0; i < newSymbols.size(); i++) {
288
                                mySymbols[i] = newSymbols.get(i).sym;
289
                        }
290
                        return mySymbols;
291
                }
292
                return null;
293
        }
294

    
295

    
296

    
297
        public ISymbol getDefaultSymbol() {
298
                if(defaultSymbol==null) {
299

    
300
                    defaultSymbol = MapContextLocator.getSymbolManager(
301
                        ).createSymbol(shapeType);
302
                        fireDefaultSymbolChangedEvent(new SymbolLegendEvent(null, defaultSymbol));
303
                }
304
                return defaultSymbol;
305
        }
306

    
307

    
308
        public String getClassName() {
309
                return getClass().getName();
310
        }
311

    
312

    
313
        public int getShapeType() {
314
                return shapeType;
315
        }
316

    
317
        public boolean isUseDefaultSymbol() {
318
                return useDefaultSymbol;
319
        }
320

    
321
        public void setDefaultSymbol(ISymbol s) throws IllegalArgumentException {
322
                if (s == null) throw new NullPointerException("Default symbol cannot be null");
323
                ISymbol old = defaultSymbol;
324
                defaultSymbol = s;
325
                fireDefaultSymbolChangedEvent(new SymbolLegendEvent(old, defaultSymbol));
326
        }
327

    
328
        public void setShapeType(int shapeType) {
329
                if (this.shapeType != shapeType) {
330

    
331
                    ISymbol sym = MapContextLocator.getSymbolManager(
332
                        ).createSymbol(shapeType);
333
                        setDefaultSymbol(sym);
334
                        this.shapeType = shapeType;
335
                }
336
        }
337

    
338
        public void useDefaultSymbol(boolean b) {
339
                useDefaultSymbol = b;
340
        }
341

    
342
        public Object[] getValues() {
343
                if (newSymbols != null) {
344
                        Object[] myObjects = new Object[newSymbols.size()];
345
                        for (int i = 0; i < newSymbols.size(); i++) {
346
                                myObjects[i] = newSymbols.get(i).getStringExpression();
347
                        }
348
                        return myObjects;
349
                }
350
                return null;
351
        }
352

    
353
    public static boolean isPolygonal(int ty) {
354
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
355
        return geomManager.isSubtype(Geometry.TYPES.MULTISURFACE, ty) || 
356
            geomManager.isSubtype(Geometry.TYPES.SURFACE, ty);
357
    }
358

    
359

    
360
    public static boolean isLinear(int ty) {
361
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
362
        return geomManager.isSubtype(Geometry.TYPES.MULTICURVE, ty) || 
363
            geomManager.isSubtype(Geometry.TYPES.CURVE, ty);
364
    }
365
    
366
    public static boolean isPoint(int ty) {
367
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
368
        return geomManager.isSubtype(Geometry.TYPES.MULTIPOINT, ty) || 
369
            geomManager.isSubtype(Geometry.TYPES.POINT, ty);
370
    }
371
    
372
    public void removeDefaultSymbol() {
373

    
374
    }
375

    
376
    // =============================
377

    
378
    public static class RegisterPersistence implements Callable {
379

    
380
        public Object call() throws Exception {
381

    
382
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
383
            if (manager.getDefinition(
384
                FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME) == null) {
385
                DynStruct definition = manager
386
                    .addDefinition(VectorFilterExpressionLegend.class,
387
                        FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME,
388
                        FILTER_EXPRESSION_LEGEND_PERSISTENCE_DEFINITION_NAME
389
                        + " Persistence definition", null, null);
390

    
391
                definition.extend(manager.getDefinition(
392
                    AbstractClassifiedVectorLegend
393
                    .CLASSIFIED_VECTOR_LEGEND_PERSISTENCE_DEFINITION_NAME));
394

    
395
                definition.addDynFieldBoolean("useDefaultSymbol")
396
                .setMandatory(true);
397
                definition.addDynFieldObject("defaultSymbol")
398
                .setClassOfValue(ISymbol.class).setMandatory(true);
399
                definition.addDynFieldInt("shapeType")
400
                .setMandatory(true);
401

    
402
                definition.addDynFieldArray("itemSymbolArray")
403
                .setClassOfItems(ISymbol.class);
404
                definition.addDynFieldArray("itemStringArray")
405
                .setClassOfItems(String.class);
406

    
407
            }
408
            return Boolean.TRUE;
409
        }
410

    
411
    }
412

    
413
    public static class RegisterLegend implements Callable {
414

    
415
        public Object call() throws Exception {
416
            MapContextManager manager =
417
                MapContextLocator.getMapContextManager();
418

    
419
            manager.registerLegend(
420
                FILTER_EXPRESSION_LEGEND_NAME,
421
                VectorFilterExpressionLegend.class);
422

    
423
            return Boolean.TRUE;
424
        }
425

    
426
    }
427

    
428
    public void saveToState(PersistentState state) throws PersistenceException {
429

    
430
        super.saveToState(state);
431
        state.set("useDefaultSymbol", this.isUseDefaultSymbol());
432
        state.set("defaultSymbol", this.getDefaultSymbol());
433
        state.set("shapeType", this.getShapeType());
434

    
435
        ISymbol[] syms = this.getSymbols();
436
        if (syms == null) {
437
            syms = new ISymbol[0];
438
        }
439
        Object[] vals = this.getValues();
440
        String[] vals_str = null;
441
        if (vals == null) {
442
            vals_str = new String[0];
443
        } else {
444
            vals_str = new String[vals.length];
445
            for (int i=0; i<vals.length; i++) {
446
                String aux = ((vals[i] == null) ? null : vals[i].toString());
447
                // Prevents saving localized version of 'Default'
448
                aux = translateDefault(aux, false);
449
                vals_str[i] = aux;
450
            }
451
        }
452

    
453
        state.set("itemSymbolArray", syms);
454
        state.set("itemStringArray", vals_str);
455

    
456
    }
457

    
458
    public void loadFromState(PersistentState state)
459
        throws PersistenceException {
460

    
461
        super.loadFromState(state);
462

    
463
        this.setShapeType(state.getInt("shapeType"));
464
        Boolean b = state.getBoolean("useDefaultSymbol");
465
        this.useDefaultSymbol(b);
466
        ISymbol defsym = (ISymbol) state.get("defaultSymbol");
467
        this.setDefaultSymbol(defsym);
468

    
469
        String[] strs = state.getStringArray("itemStringArray");
470
        ISymbol[] syms = (ISymbol[]) state.getArray("itemSymbolArray",
471
            ISymbol.class);
472

    
473
        if (strs.length != syms.length) {
474
            logger.info("VectorFilterExpressionLegend - load state - Different size in arrays: " + strs.length + ", " + syms.length);
475
        }
476
        int nmin = Math.min(strs.length, syms.length);
477
        for (int i=0; i<nmin; i++) {
478
            String aux = strs[i];
479
            aux = translateDefault(aux, true);
480
            this.addSymbol(aux, syms[i]);
481
        }
482
    }
483

    
484
    /**
485
     * Utility method to (un)translate the word 'Default'
486
     * @param aux
487
     * @param forward
488
     * If TRUE, then translate (Default -> Por defecto)
489
     * If FALSE then untranslate (Por defecto -> Default)
490
     * @return
491
     */
492
    private String translateDefault(String aux, boolean forward) {
493

    
494
        if (aux == null) {
495
            return null;
496
        }
497
        if (forward && aux.compareTo(NON_I18N_DEFAULT) == 0) {
498
            return I18N_DEFAULT;
499
        }
500
        if (!forward && aux.compareTo(I18N_DEFAULT) == 0) {
501
            return NON_I18N_DEFAULT;
502
        }
503
        return aux;
504
    }
505

    
506

    
507
    public Object clone() throws CloneNotSupportedException {
508

    
509
        VectorFilterExpressionLegend resp =
510
            (VectorFilterExpressionLegend) super.clone();
511

    
512
        Object[] vals = this.getValues();
513
        ISymbol[] syms = this.getSymbols();
514
        if (vals != null && syms != null) {
515

    
516
            resp.resetItems();
517

    
518
            int n = Math.min(vals.length, syms.length);
519
            for (int i=0; i<n; i++) {
520
                resp.addSymbol(
521
                    vals[i],
522
                    (ISymbol) syms[i].clone());
523
            }
524
        }
525
        ISymbol sym = this.getDefaultSymbol();
526
        sym = (ISymbol) sym.clone();
527
        resp.setDefaultSymbol(sym);
528
        return resp;
529
    }
530

    
531

    
532
  public static void selfRegister() {
533
    Caller caller = new DefaultCaller();
534

    
535
    caller.add(new VectorFilterExpressionLegend.RegisterLegend());
536
    caller.add(new VectorFilterExpressionLegend.RegisterPersistence());
537

    
538
    if (!caller.call()) {
539
      throw new RuntimeException(
540
              "Can't register VectorFilterExpressionLegend",
541
              caller.getException()
542
      );
543
    }
544
    MapContextManager mcoman = MapContextLocator.getMapContextManager();
545
    mcoman.registerLegendWriter(
546
        VectorFilterExpressionLegend.class,
547
            SymbolManager.LEGEND_FILE_EXTENSION.substring(1),
548
            PersistenceBasedLegendWriter.class);
549
    
550
    
551
  }
552

    
553
}