Statistics
| Revision:

svn-gvsig-desktop / trunk / extensions / extSymbology / src / org / gvsig / symbology / fmap / labeling / GeneralLabelingStrategy.java @ 30165

History | View | Annotate | Download (27.5 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2005 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

    
42
/* CVS MESSAGES:
43
 *
44
 * $Id: GeneralLabelingStrategy.java 13749 2007-09-17 14:16:11Z jaume $
45
 * $Log$
46
 * Revision 1.2  2007-09-17 14:16:11  jaume
47
 * multilayer symbols sizing bug fixed
48
 *
49
 * Revision 1.1  2007/05/22 12:17:41  jaume
50
 * *** empty log message ***
51
 *
52
 * Revision 1.1  2007/05/22 10:05:31  jaume
53
 * *** empty log message ***
54
 *
55
 * Revision 1.10  2007/05/17 09:32:06  jaume
56
 * *** empty log message ***
57
 *
58
 * Revision 1.9  2007/05/09 11:04:58  jaume
59
 * refactored legend hierarchy
60
 *
61
 * Revision 1.8  2007/04/13 11:59:30  jaume
62
 * *** empty log message ***
63
 *
64
 * Revision 1.7  2007/04/12 14:28:43  jaume
65
 * basic labeling support for lines
66
 *
67
 * Revision 1.6  2007/04/11 16:01:08  jaume
68
 * maybe a label placer refactor
69
 *
70
 * Revision 1.5  2007/04/10 16:34:01  jaume
71
 * towards a styled labeling
72
 *
73
 * Revision 1.4  2007/04/02 16:34:56  jaume
74
 * Styled labeling (start commiting)
75
 *
76
 * Revision 1.3  2007/03/28 16:48:01  jaume
77
 * *** empty log message ***
78
 *
79
 * Revision 1.2  2007/03/26 14:40:38  jaume
80
 * added print method (BUT UNIMPLEMENTED)
81
 *
82
 * Revision 1.1  2007/03/20 16:16:20  jaume
83
 * refactored to use ISymbol instead of FSymbol
84
 *
85
 * Revision 1.2  2007/03/09 11:20:57  jaume
86
 * Advanced symbology (start committing)
87
 *
88
 * Revision 1.1  2007/03/09 08:33:43  jaume
89
 * *** empty log message ***
90
 *
91
 * Revision 1.1.2.5  2007/02/21 07:34:08  jaume
92
 * labeling starts working
93
 *
94
 * Revision 1.1.2.4  2007/02/15 16:23:44  jaume
95
 * *** empty log message ***
96
 *
97
 * Revision 1.1.2.3  2007/02/09 07:47:05  jaume
98
 * Isymbol moved
99
 *
100
 * Revision 1.1.2.2  2007/02/02 16:21:24  jaume
101
 * start commiting labeling stuff
102
 *
103
 * Revision 1.1.2.1  2007/02/01 17:46:49  jaume
104
 * *** empty log message ***
105
 *
106
 *
107
 */
108
package org.gvsig.symbology.fmap.labeling;
109

    
110
import java.awt.Graphics2D;
111
import java.awt.Shape;
112
import java.awt.geom.Rectangle2D;
113
import java.awt.image.BufferedImage;
114
import java.io.StringReader;
115
import java.text.NumberFormat;
116
import java.util.ArrayList;
117
import java.util.Hashtable;
118
import java.util.Iterator;
119
import java.util.TreeMap;
120
import java.util.TreeSet;
121

    
122
import javax.print.attribute.PrintRequestAttributeSet;
123
import javax.print.attribute.standard.PrintQuality;
124

    
125
import org.apache.log4j.Logger;
126
import org.cresques.cts.ICoordTrans;
127
import org.gvsig.symbology.fmap.labeling.parse.LabelExpressionParser;
128
import org.gvsig.symbology.fmap.labeling.parse.ParseException;
129
import org.gvsig.symbology.fmap.labeling.placements.ILabelPlacement;
130
import org.gvsig.symbology.fmap.labeling.placements.LinePlacementConstraints;
131
import org.gvsig.symbology.fmap.labeling.placements.MultiShapePlacementConstraints;
132
import org.gvsig.symbology.fmap.labeling.placements.PointPlacementConstraints;
133
import org.gvsig.symbology.fmap.labeling.placements.PolygonPlacementConstraints;
134
import org.gvsig.symbology.fmap.labeling.placements.RemoveDuplicatesComparator;
135
import org.gvsig.symbology.fmap.rendering.filter.operations.Expression;
136
import org.gvsig.symbology.fmap.rendering.filter.operations.ExpressionException;
137
import org.gvsig.symbology.fmap.symbols.SmartTextSymbol;
138

    
139
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
140
import com.hardcode.gdbms.engine.values.Value;
141
import com.iver.andami.PluginServices;
142
import com.iver.cit.gvsig.fmap.MapContext;
143
import com.iver.cit.gvsig.fmap.ViewPort;
144
import com.iver.cit.gvsig.fmap.core.CartographicSupport;
145
import com.iver.cit.gvsig.fmap.core.CartographicSupportToolkit;
146
import com.iver.cit.gvsig.fmap.core.FNullGeometry;
147
import com.iver.cit.gvsig.fmap.core.FPoint2D;
148
import com.iver.cit.gvsig.fmap.core.FShape;
149
import com.iver.cit.gvsig.fmap.core.IFeature;
150
import com.iver.cit.gvsig.fmap.core.IGeometry;
151
import com.iver.cit.gvsig.fmap.core.ShapeFactory;
152
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
153
import com.iver.cit.gvsig.fmap.drivers.IFeatureIterator;
154
import com.iver.cit.gvsig.fmap.layers.FLayer;
155
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
156
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.ILabelingMethod;
157
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.ILabelingStrategy;
158
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.IPlacementConstraints;
159
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.IZoomConstraints;
160
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.LabelClass;
161
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.LabelLocationMetrics;
162
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.LabelingFactory;
163
import com.iver.utiles.XMLEntity;
164
import com.iver.utiles.swing.threads.Cancellable;
165
import com.vividsolutions.jts.geom.Geometry;
166

    
167
/**
168
 *
169
 * GeneralLabelingStrategy.java
170
 *
171
 *
172
 * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 4, 2008
173
 *
174
 */
175
public class GeneralLabelingStrategy implements ILabelingStrategy, Cloneable,CartographicSupport {
176
        public static IPlacementConstraints DefaultPointPlacementConstraints = new PointPlacementConstraints();
177
        public static IPlacementConstraints DefaultLinePlacementConstraints = new LinePlacementConstraints();
178
        public static IPlacementConstraints DefaultPolygonPlacementConstraints = new PolygonPlacementConstraints();
179
        private static String[] NO_TEXT = {PluginServices.getText(null, "text_field")};
180
        private static MultiShapePlacementConstraints DefaultMultiShapePlacementConstratints = new MultiShapePlacementConstraints();
181
        private ILabelingMethod method;
182
        private IPlacementConstraints placementConstraints;
183
        protected FLyrVect layer;
184
        private IZoomConstraints zoomConstraints;
185
        private boolean allowOverlapping;
186
        private long parseTime;
187
        private int unit;
188
        private int referenceSystem;
189
        private double sizeAfter;
190

    
191
        public void setLayer(FLayer layer) throws ReadDriverException {
192
                FLyrVect l = (FLyrVect) layer;
193
                this.layer = l;
194
        }
195

    
196
        public ILabelingMethod getLabelingMethod() {
197
                return method;
198
        }
199

    
200
        public void setLabelingMethod(ILabelingMethod method) {
201
                this.method = method;
202
        }
203

    
204
        private class GeometryItem{
205
                public IGeometry geom = null;
206
                public int weigh = 0;
207

    
208
                public GeometryItem(IGeometry geom, int weigh){
209
                        this.geom = geom;
210
                        this.weigh = weigh;
211
                }
212
        }
213
        public void draw(BufferedImage mapImage, Graphics2D mapGraphics,
214
                        ViewPort viewPort,        Cancellable cancel, double dpi) throws ReadDriverException {
215

    
216
//                boolean bVisualFXEnabled = false; // if true, the user can see how the labeling is drawing up
217

    
218

    
219
                TreeMap<String[], GeometryItem> labelsToPlace = null;
220
                parseTime =0;
221
//                long t1 = System.currentTimeMillis();
222
                String[] usedFields = getUsedFields();
223

    
224
                int notPlacedCount = 0;
225
                int placedCount = 0;
226

    
227
                /*
228
                 * Get the label placement solvers according the user's settings
229
                 */
230
                ILabelPlacement placement = PlacementManager.getPlacement(getPlacementConstraints(), layer.getShapeType());
231

    
232

    
233
                BufferedImage targetBI;
234
                Graphics2D targetGr;
235

    
236

    
237
                /*
238
                 * get an ordered set of the LabelClasses up on the
239
                 * label priority
240
                 */
241
                LabelClass[] lcs = method.getLabelClasses();
242
                TreeSet<LabelClass> ts = new TreeSet<LabelClass>(new LabelClassComparatorByPriority());
243

    
244
                for (int i = 0; i < lcs.length; i++) ts.add(lcs[i]);
245

    
246
                if (ts.size()==0) return;
247

    
248
                /*
249
                 * now we have an ordered set, it is only need to give a pass
250
                 * for each label class to render by priorities.
251
                 *
252
                 * If no priorities were defined, the following loop only executes
253
                 * once
254
                 */
255
                for (LabelClass lc : ts) {
256
                        IFeatureIterator it =  method.getFeatureIteratorByLabelClass(layer, lc, viewPort, usedFields);
257

    
258
                        // duplicates treatment stuff
259
                        /* handle the duplicates mode */
260
                        int duplicateMode = getDuplicateLabelsMode();
261
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
262
                                // we need to register the labels already placed
263

    
264
                                labelsToPlace = new TreeMap<String[], GeometryItem>(new RemoveDuplicatesComparator());
265
                        }
266

    
267
                        boolean bLabelsReallocatable = !isAllowingOverlap();
268

    
269
                        BufferedImage overlapDetectImage = null;
270
                        Graphics2D overlapDetectGraphics = null;
271
                        if (bLabelsReallocatable) {
272
                                overlapDetectImage = new BufferedImage(
273
                                                viewPort.getImageWidth()+(int)viewPort.getOffset().getX(),
274
                                                viewPort.getImageHeight()+(int)viewPort.getOffset().getY(),
275
                                                BufferedImage.TYPE_INT_ARGB
276
                                );
277
                                overlapDetectGraphics = overlapDetectImage.createGraphics();
278
                                overlapDetectGraphics.setRenderingHints(mapGraphics.getRenderingHints());
279
                        }
280
                        if (bLabelsReallocatable) {
281
                                targetBI = overlapDetectImage;
282
                                targetGr = overlapDetectGraphics;
283
                        } else {
284
                                targetBI = mapImage;
285
                                targetGr = mapGraphics;
286
                        }
287

    
288

    
289

    
290
                        while ( !cancel.isCanceled() && it.hasNext()) {
291
                                IFeature feat = it.next();
292
                                IGeometry geom = feat.getGeometry();
293
                                if (geom==null || geom instanceof FNullGeometry) // we don't need to label null geometries
294
                                        continue;
295

    
296
                                if (!setupLabel(feat, lc, cancel, usedFields, viewPort, dpi, duplicateMode)){ //, placedLabels)){
297
                                        continue;
298
                                }
299

    
300
                                String[] texts = lc.getTexts();
301
                                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
302
                                        // check if this text (so label) is already present in the map
303

    
304
                                        GeometryItem item = labelsToPlace.get(texts);
305
                                        if (item == null){
306
                                                item = new GeometryItem(null, 0);
307
                                                labelsToPlace.put(texts, item);
308
                                        }
309
                                        if (item.geom != null){
310
                                                notPlacedCount++;
311
                                                if(geom.getGeometryType() != FShape.POINT){
312
                                                        Geometry jtsGeom = item.geom.toJTSGeometry().union(geom.toJTSGeometry());
313
                                                        item.geom = FConverter.jts_to_igeometry(jtsGeom);
314
                                                } else {
315
                                                        int weigh = item.weigh;
316
                                                        FPoint2D pointFromLabel = (FPoint2D)item.geom.getInternalShape();
317
                                                        item.geom = ShapeFactory.createPoint2D(
318
                                                                        (pointFromLabel.getX()*weigh + ((FPoint2D)geom.getInternalShape()).getX())/(weigh+1),
319
                                                                        (pointFromLabel.getY()*weigh + ((FPoint2D)geom.getInternalShape()).getY())/(weigh+1));
320
                                                }
321
                                        } else {
322
                                                item.geom = geom;
323
                                        }
324
                                        item.weigh++;
325
                                } else {
326
                                        // Check if size is a pixel
327
                                        if (isOnePoint(viewPort, geom)) {
328
                                                continue;
329
                                        }
330
//                                        lc.toCartographicSize(viewPort, dpi, null);
331

    
332
                                        drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, geom, cancel, dpi, bLabelsReallocatable);
333
                                        placedCount++;
334
                                }
335
                        }
336
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
337
                                Iterator<String[]> textsIt = labelsToPlace.keySet().iterator();
338
                                while ( !cancel.isCanceled() && textsIt.hasNext()) {
339
                                        notPlacedCount++;
340
                                        String[] texts = textsIt.next();
341

    
342
                                        GeometryItem item = labelsToPlace.get(texts);
343
                                        if(item != null){
344
                                                lc.setTexts(texts);
345
                                                // Check if size is a pixel
346
                                                if (isOnePoint(viewPort, item.geom)) {
347
                                                        continue;
348
                                                }
349

    
350
//                                                lc.toCartographicSize(viewPort, dpi, null);
351
                                                drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, item.geom, cancel, dpi, bLabelsReallocatable);
352
                                        }
353
                                }
354
                        }
355

    
356
                        if (bLabelsReallocatable) {
357
                                mapGraphics.drawImage(overlapDetectImage, null, null);
358
                        }
359

    
360

    
361
                }
362
//                double totalTime = (System.currentTimeMillis()-t1);
363
//
364
//                int total = placedCount+notPlacedCount;
365
//
366
//                if (total>0)
367
//                Logger.getLogger(getClass()).info("Labeled layer '"+layer.getName()+
368
//                "' "+totalTime/1000D+" seconds. "+placedCount+"/"+total+
369
//                " labels placed ("+NumberFormat.getInstance().
370
//                format(100*placedCount/(double) total)+"%)");
371
//
372
//                if (cancel.isCanceled()) {
373
//                        Logger.getLogger(getClass()).info("Layer labeling canceled: '"+
374
//                                        layer.getName()+"'");
375
//                } else {
376
//                        Logger.getLogger(getClass()).info("Total labels parse time = "+
377
//                                        parseTime+" ("+NumberFormat.getInstance().
378
//                                        format(parseTime*100/totalTime)+"%)");
379
//                }
380

    
381
        }
382
        private void drawLabelInGeom(BufferedImage targetBI, Graphics2D targetGr, LabelClass lc,
383
                        ILabelPlacement placement, ViewPort viewPort, IGeometry geom, Cancellable cancel,
384
                        double dpi, boolean bLabelsReallocatable){
385

    
386
                lc.toCartographicSize(viewPort, dpi, null);
387

    
388
                ArrayList<LabelLocationMetrics> llm = null;
389

    
390
                llm = placement.guess(
391
                                lc,
392
                                geom,
393
                                getPlacementConstraints(),
394
                                0,
395
                                cancel,viewPort);
396

    
397
                setReferenceSystem(lc.getReferenceSystem());
398
                setUnit(lc.getUnit());
399

    
400
                /*
401
                 *  Esto provoca errores en el calculo del tama?o de la LabelClass
402
                 * as? que se ha diferido el calculo del tama?o con que deber?a
403
                 * dibujarse el texto justo hasta el momento en que se dibuja ?ste,
404
                 * en el metodo draw de la LabelClass.
405
                 *
406
                 * FIXME: Mantengo el c?digo viejo comentarizado para enfatizar
407
                 * este comentario. Eliminar cuando se asuma que es correcto el cambio.
408
                 *
409
                 */
410

    
411
//                double sizeBefore = lc.getTextSymbol().getFont().getSize();
412

    
413
//                sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
414
//                                sizeBefore,
415
//                                viewPort,
416
//                                MapContext.getScreenDPI());
417

    
418

    
419
                /*
420
                 * search if there is room left by the previous and
421
                 * with more priority labels, then check the current
422
                 * level
423
                 */
424
//                if (
425
                lookupAndPlaceLabel(targetBI, targetGr, llm,
426
                                placement, lc, geom, viewPort, cancel,        bLabelsReallocatable);  //{
427

    
428
//                        lc.getTextSymbol().setFontSize(sizeBefore);
429
//                }
430
//                lc.toCartographicSize(viewPort, dpi, null);
431

    
432
        }
433

    
434
        private int getDuplicateLabelsMode() {
435
                if (getPlacementConstraints() == null) {
436
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
437
                }
438
                return getPlacementConstraints().getDuplicateLabelsMode();
439
        }
440

    
441
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
442
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
443
                        LabelClass lc, IGeometry geom,        ViewPort viewPort,
444
                        Cancellable cancel, boolean bLabelsReallocatable) {
445
                int i;
446

    
447
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
448
                        LabelLocationMetrics labelMetrics = llm.get(i);
449

    
450
                        IPlacementConstraints pc = getPlacementConstraints();
451
                        if(pc instanceof MultiShapePlacementConstraints){
452
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints)pc;
453
                                int shapeType = geom.getGeometryType();
454
                                switch (shapeType % FShape.Z) {
455
                                case FShape.POINT:
456
                                        pc = mpc.getPointConstraints();
457
                                        break;
458
                                case FShape.LINE:
459
                                        pc = mpc.getLineConstraints();
460
                                        break;
461
                                case FShape.POLYGON:
462
                                        pc = mpc.getPolygonConstraints();
463
                                        break;
464
                                }                
465
                        }
466
                        
467
                        /*
468
                         * Ver comentario en el metodo drawLabelInGeom
469
                         */
470
//                        lc.getTextSymbol().setFontSize(sizeAfter);// * FConstant.FONT_HEIGHT_SCALE_FACTOR);
471
                        if (bLabelsReallocatable) {
472
                                if (!isOverlapping(bi, lc.getShape(labelMetrics))) {
473

    
474
                                        if(!pc.isFollowingLine()){
475
                                                lc.draw(g, labelMetrics, (FShape) geom.getInternalShape());
476
                                        }
477
                                        else{
478
                                                SmartTextSymbolLabelClass smsLc = new SmartTextSymbolLabelClass();
479
                                                SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
480

    
481
                                                double sizeBefore = lc.getTextSymbol().getFont().getSize();
482
                                                double sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
483
                                                                sizeBefore,
484
                                                                viewPort,
485
                                                                MapContext.getScreenDPI());
486
                                                sms.setFontSize(sizeAfter);
487

    
488
                                                smsLc.setTextSymbol(sms);
489
                                                geom.transform(viewPort.getAffineTransform());
490
                                                smsLc.draw(g, null, (FShape) geom.getInternalShape());
491
                                                sms.setFontSize(sizeBefore);
492

    
493
                                        }
494
                                        return true;
495
                                }
496
                        } else {
497
                                if(!pc.isFollowingLine()){
498
                                        lc.draw(g, labelMetrics, null);
499
                                }
500
                                else{
501
                                        SmartTextSymbolLabelClass smsLc = new SmartTextSymbolLabelClass();
502
                                        SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
503

    
504
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
505
                                        double sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
506
                                                        sizeBefore,
507
                                                        viewPort,
508
                                                        MapContext.getScreenDPI());
509
                                        sms.setFontSize(sizeAfter);
510

    
511
                                        smsLc.setTextSymbol(sms);
512
                                        geom.transform(viewPort.getAffineTransform());
513
                                        smsLc.draw(g, null, (FShape) geom.getInternalShape());
514

    
515
                                        sms.setFontSize(sizeBefore);
516
                                }
517
                                return true;
518
                        }
519
                }
520
                return false;
521
        }
522

    
523
        /**
524
         * Divide una cadena de caracteres por el caracter dos puntos siempre que no est? entre comillas.
525
         *
526
         * @param str
527
         *            Cadena de caracteres
528
         *
529
         * @return String[]
530
         *
531
         */
532
        private String[] divideExpression(String str){
533
                ArrayList<String> r = new ArrayList<String>();
534
                boolean inQuotationMarks = false;
535
                int lastIndex = 0;
536
                for(int i=0; i<str.length(); i++){
537
                        if(str.substring(i, i+1).compareTo("\"")==0){
538
                                inQuotationMarks = !inQuotationMarks;
539
                                continue;
540
                        }
541
                        if(str.substring(i, i+1).compareTo(":")==0 && !inQuotationMarks){
542
                                if(lastIndex < i){
543
                                        r.add(str.substring(lastIndex, i));
544
                                }
545
                                lastIndex = i+1;
546
                        }
547
                }
548
                if(lastIndex < str.length()-1){
549
                        r.add(str.substring(lastIndex));
550
                }
551
                String[] result = new String[r.size()];
552
                r.toArray(result);
553
                return result;
554
        }
555

    
556
        /**
557
         * Compute the texts to show in the label and store them in LabelClass.
558
         */
559
        @SuppressWarnings("unchecked")
560
        private boolean setupLabel(IFeature feat, LabelClass lc,
561
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
562
                        double dpi, int duplicateMode){//, TreeSet<?> placedLabels) {
563

    
564
                Value[] vv = feat.getAttributes();
565
                String expr = lc.getStringLabelExpression();
566

    
567
                long pt1 = System.currentTimeMillis();
568
                String[] texts = NO_TEXT;
569
                ArrayList<String> preTexts = new ArrayList<String>();
570
//                String[] texts = {PluginServices.getText(this, "text_field")};
571
                try {
572

    
573
                        for (int i = 0; !cancel.isCanceled() && i < usedFields.length; i++) {
574
                                try {
575
                                        int index = layer.getSource().getRecordset().getFieldIndexByName(usedFields[i]);
576
                                        if(index != -1)
577
                                                symbol_table.put(usedFields[i], feat.getAttribute(index));
578
                                } catch (ReadDriverException e) {
579
                                        // TODO Auto-generated catch block
580
                                        e.printStackTrace();
581
                                }
582
                        }
583

    
584
                        if (expr != null) {
585

    
586
                                if(expr.equals("") || expr.equals(LabelExpressionParser.tokenFor(LabelExpressionParser.EOEXPR)))
587
                                        expr = texts[0];
588

    
589
                                /*
590
                                 * FIXME: Esto es un parche.
591
                                 * Parece que el LabelExpresionParser no es capaz de
592
                                 * de dividir convenientemente una expression compuesta por varias expresiones
593
                                 * separadas por ":".
594
                                 * Habr?a que arreglar (con tiempo) el evaluador, pero,
595
                                 * de momento, rodeamos el problema.
596
                                 */
597
                                String[] multiexpr = divideExpression(expr);
598
                                for(int i=0; i<multiexpr.length; i++){
599
                                        expr = multiexpr[i];
600
                                        if(!expr.endsWith(";")) expr += ";";
601
                                        // parse (if it hasn't been parsed yet) and evaluate the expression
602
                                        Expression evaluator = getEvaluator(expr);
603
                                        Object labelContents = evaluator.evaluate();
604
                                        if (labelContents!=null) {
605
                                                if (String[].class.equals(labelContents.getClass())) {
606
                                                        for(int j=0; j<((String[]) labelContents).length;j++){
607
                                                                preTexts.add(((String[])labelContents)[j]);
608
                                                        }
609
                                                } else {
610
                                                        preTexts.add(labelContents.toString());
611
                                                }
612
                                        }
613
                                }
614
                                texts = new String[preTexts.size()];
615
                                preTexts.toArray(texts);
616
                                parseTime += System.currentTimeMillis()-pt1;
617
                        }
618
                        lc.setTexts(texts);
619

    
620
                } catch (ExpressionException e) {
621
                        e.printStackTrace();
622
                        return false;
623
                }
624
                return true;
625
        }
626
        private Hashtable<String, Value> symbol_table = new Hashtable<String, Value>();
627
        private Hashtable<String, Expression> evaluators = new Hashtable<String, Expression>();
628

    
629
        private Expression getEvaluator(String strExpr) {
630
                Expression expr = evaluators.get(strExpr);
631
                if (expr == null) {
632
                        LabelExpressionParser p = new LabelExpressionParser(
633
                                        new StringReader(strExpr),symbol_table);
634

    
635
                        try {
636
                                p.LabelExpression();
637
                        } catch (ParseException e) {
638
                                e.printStackTrace();
639
                        }
640
                        expr = (Expression) p.getStack().pop();
641
                        evaluators.put(strExpr, expr);
642
                }
643
                return expr;
644
        }
645

    
646
        private boolean isOverlapping(BufferedImage bi, FShape labelShape) {
647
                if (labelShape==null)
648
                        return false;
649
                Rectangle2D rPixels = labelShape.getBounds2D();
650
                for (int i= (int) rPixels.getX(); i<=rPixels.getMaxX(); i++){
651
                        for (int j= (int) rPixels.getY(); j<=rPixels.getMaxY(); j++){
652

    
653
                                if (!labelShape.contains(i, j) // contains seems to don't detect points placed in the rectangle boundaries
654
                                                && !labelShape.intersects(i, j, i, j)) {
655
                                        continue;
656
                                }
657

    
658
                                if (i<0 || j<0) {
659
                                        continue;
660
                                }
661

    
662
                                if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
663
                                        continue;
664
                                }
665

    
666
                                if (bi.getRGB(i,j)!=0){
667
                                        return true;
668
                                }
669
                        }
670
                }
671
                return false;
672
        }
673

    
674
        private boolean isOnePoint(ViewPort viewPort, IGeometry geom) {
675
                boolean onePoint = false;
676
                int shapeType = geom.getGeometryType();
677
                if ((shapeType % FShape.Z)!=FShape.POINT && (shapeType % FShape.Z) !=FShape.MULTIPOINT) {
678

    
679
                        Rectangle2D geomBounds = geom.getBounds2D();
680
//                        ICoordTrans ct = layer.getCoordTrans();
681
//
682
//                        if (ct!=null) {
683
//                                geomBounds = ct.convert(geomBounds);
684
//                        }
685

    
686
                        double dist1Pixel = viewPort.getDist1pixel();
687
                        onePoint = (geomBounds.getWidth() <= dist1Pixel
688
                                        && geomBounds.getHeight() <= dist1Pixel);
689
                }
690
                return onePoint;
691
        }
692

    
693
        public String getClassName() {
694
                return getClass().getName();
695
        }
696

    
697
        public XMLEntity getXMLEntity() {
698
                XMLEntity xml = new XMLEntity();
699
                xml.putProperty("labelingStrategy", "labelingStrategy");
700
                xml.putProperty("className", getClassName());
701
                xml.putProperty("allowsOverlapping", allowOverlapping);
702
//                xml.putProperty("minScaleView", minScaleView);
703
//                xml.putProperty("maxScaleView", maxScaleView);
704

    
705
                if (method!=null) {
706
                        XMLEntity methodEntity = method.getXMLEntity();
707
                        methodEntity.putProperty("id", "LabelingMethod");
708
                        xml.addChild(methodEntity);
709
                }
710

    
711
                if (placementConstraints != null) {
712
                        XMLEntity pcEntity = placementConstraints.getXMLEntity();
713
                        pcEntity.putProperty("id", "PlacementConstraints");
714
                        xml.addChild(pcEntity);
715
                }
716

    
717
                if (zoomConstraints != null) {
718
                        XMLEntity zcEntity = zoomConstraints.getXMLEntity();
719
                        zcEntity.putProperty("id", "ZoomConstraints");
720
                        xml.addChild(zcEntity);
721
                }
722
                return xml;
723
        }
724

    
725
        public void setXMLEntity(XMLEntity xml) {
726
                XMLEntity aux = xml.firstChild("id", "LabelingMethod");
727

    
728
                // overlapping mode
729
                if (xml.contains("allowsOverlapping")) {
730
                        allowOverlapping = xml.getBooleanProperty("allowsOverlapping");
731
                }
732

    
733

    
734
                if (aux != null) {
735
                        method = LabelingFactory.createMethodFromXML(aux);
736
                }
737

    
738
                aux = xml.firstChild("id", "PlacementConstraints");
739
                if (aux != null) {
740
                        placementConstraints = LabelingFactory.createPlacementConstraintsFromXML(aux);
741
                }
742

    
743
                aux = xml.firstChild("id", "ZoomConstraints");
744
                if (aux != null) {
745
                        zoomConstraints = LabelingFactory.createZoomConstraintsFromXML(aux);
746
                }
747
        }
748

    
749
        public boolean isAllowingOverlap() {
750
                return allowOverlapping;
751
        }
752

    
753
        public void setAllowOverlapping(boolean allowOverlapping) {
754
                this.allowOverlapping = allowOverlapping;
755
        }
756

    
757
        public IPlacementConstraints getPlacementConstraints() {
758
                if (placementConstraints != null)
759
                        return placementConstraints;
760

    
761
                try {
762
                        switch (layer.getShapeType() % FShape.Z) {
763
                        case FShape.POINT:
764
                        case FShape.MULTIPOINT:
765
                                return DefaultPointPlacementConstraints;
766
                        case FShape.LINE:
767
                                return DefaultLinePlacementConstraints;
768
                        case FShape.POLYGON:
769
                                return DefaultPolygonPlacementConstraints;
770
                        case FShape.MULTI:
771
                                DefaultMultiShapePlacementConstratints.setPointConstraints(DefaultPointPlacementConstraints);
772
                                DefaultMultiShapePlacementConstratints.setLineConstraints(DefaultLinePlacementConstraints);
773
                                DefaultMultiShapePlacementConstratints.setPolygonConstraints(DefaultPolygonPlacementConstraints);
774
                                return DefaultMultiShapePlacementConstratints;
775
                        }
776

    
777
                } catch (ReadDriverException e) {
778

    
779
                }
780
                return null;
781
        }
782

    
783
        public void setPlacementConstraints(IPlacementConstraints constraints) {
784
                this.placementConstraints = constraints;
785
        }
786

    
787
        public IZoomConstraints getZoomConstraints() {
788
                return zoomConstraints;
789
        }
790

    
791
        public void setZoomConstraints(IZoomConstraints constraints) {
792
                this.zoomConstraints = constraints;
793
        }
794

    
795
        public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel, PrintRequestAttributeSet properties) throws ReadDriverException {
796
                double dpi = 100;
797

    
798
                PrintQuality resolution=(PrintQuality)properties.get(PrintQuality.class);
799
                if (resolution.equals(PrintQuality.NORMAL)){
800
                        dpi = 300;
801
                } else if (resolution.equals(PrintQuality.HIGH)){
802
                        dpi = 600;
803
                } else if (resolution.equals(PrintQuality.DRAFT)){
804
                        dpi = 72;
805
                }
806

    
807
                draw(null,g,viewPort,cancel,dpi);
808
        }
809

    
810
        public String[] getUsedFields() {
811
                LabelClass[] lcs = method.getLabelClasses();
812
                ArrayList<String> fieldNames = new ArrayList<String>();
813
                for (int i = 0; i < lcs.length; i++) {
814
                        if(lcs[i].getLabelExpressions() != null){
815
                                for (int j = 0; j < lcs[i].getLabelExpressions().length; j++) {
816
                                        String expr = lcs[i].getLabelExpressions()[j];
817
                                        int start;
818
                                        while (expr != null &&
819
                                                        (start = expr.indexOf("[")) != -1) {
820
                                                int end = expr.indexOf("]");
821
                                                String field = expr.substring(start+1, end).trim();
822
                                                if (!fieldNames.contains(field))
823
                                                        fieldNames.add(field);
824
                                                expr = expr.substring(end+1, expr.length());
825
                                        }
826
                                }
827
                        }
828
                }
829
                return fieldNames.toArray(new String[fieldNames.size()]);
830
        }
831

    
832

    
833
        public boolean shouldDrawLabels(double scale) {
834
                double minScaleView = -1;
835
                double maxScaleView = -1;
836

    
837
                if (zoomConstraints != null) {
838
                        minScaleView = zoomConstraints.getMinScale();
839
                        maxScaleView = zoomConstraints.getMaxScale();
840
                }
841

    
842
                if (minScaleView == -1 && maxScaleView == -1) {
843
                        // parameters not set, so the layer decides.
844
                        return layer.isWithinScale(scale);
845
                }
846

    
847
                if (minScaleView >= scale) {
848
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
849
                }
850

    
851
                return false;
852
        }
853

    
854
        public void setUnit(int unitIndex) {
855
                unit = unitIndex;
856

    
857
        }
858

    
859
        public int getUnit() {
860
                return unit;
861
        }
862

    
863
        public int getReferenceSystem() {
864
                return referenceSystem;
865
        }
866

    
867
        public void setReferenceSystem(int referenceSystem) {
868
                this.referenceSystem = referenceSystem;
869
        }
870

    
871
        public double toCartographicSize(ViewPort viewPort, double dpi, FShape shp) {
872
                // TODO Auto-generated method stub
873
                return 0;
874
        }
875

    
876
        public void setCartographicSize(double cartographicSize, FShape shp) {
877
                // TODO Auto-generated method stub
878

    
879
        }
880

    
881
        public double getCartographicSize(ViewPort viewPort, double dpi, FShape shp) {
882
                // TODO Auto-generated method stub
883
                return 0;
884
        }
885

    
886
}