Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.labeling.app / org.gvsig.labeling.app.mainplugin / src / main / java / org / gvsig / labeling / label / GeneralLabelingStrategy.java @ 40673

History | View | Annotate | Download (30.1 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.labeling.label;
109

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

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

    
126
import org.gvsig.fmap.dal.exception.ReadException;
127
import org.gvsig.fmap.dal.feature.Feature;
128
import org.gvsig.fmap.dal.feature.FeatureSet;
129
import org.gvsig.fmap.geom.Geometry;
130
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
131
import org.gvsig.fmap.geom.GeometryLocator;
132
import org.gvsig.fmap.geom.Geometry.TYPES;
133
import org.gvsig.fmap.geom.GeometryManager;
134
import org.gvsig.fmap.geom.primitive.Envelope;
135
import org.gvsig.fmap.geom.primitive.Point;
136
import org.gvsig.fmap.geom.type.GeometryType;
137
import org.gvsig.fmap.mapcontext.ViewPort;
138
import org.gvsig.fmap.mapcontext.layers.FLayer;
139
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
140
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelClass;
141
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingMethod;
142
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingStrategy;
143
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IPlacementConstraints;
144
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IZoomConstraints;
145
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
146
import org.gvsig.i18n.Messages;
147
import org.gvsig.labeling.operations.Expression;
148
import org.gvsig.labeling.operations.ExpressionException;
149
import org.gvsig.labeling.parse.LabelExpressionParser;
150
import org.gvsig.labeling.placements.ILabelPlacement;
151
import org.gvsig.labeling.placements.LinePlacementConstraints;
152
import org.gvsig.labeling.placements.MultiShapePlacementConstraints;
153
import org.gvsig.labeling.placements.PlacementManager;
154
import org.gvsig.labeling.placements.PointPlacementConstraints;
155
import org.gvsig.labeling.placements.PolygonPlacementConstraints;
156
import org.gvsig.labeling.placements.RemoveDuplicatesComparator;
157
import org.gvsig.labeling.symbol.SmartTextSymbolLabelClass;
158
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.styling.LabelLocationMetrics;
159
import org.gvsig.tools.dispose.DisposableIterator;
160
import org.gvsig.tools.task.Cancellable;
161
import org.slf4j.Logger;
162
import org.slf4j.LoggerFactory;
163

    
164
import com.vividsolutions.jts.geom.CoordinateSequence;
165
import com.vividsolutions.jts.geom.CoordinateSequences;
166
import com.vividsolutions.jts.geom.GeometryFactory;
167
import com.vividsolutions.jts.geom.LineString;
168
import com.vividsolutions.jts.geom.Lineal;
169

    
170
/**
171
 *
172
 * GeneralLabelingStrategy.java
173
 *
174
 *
175
 * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 4, 2008
176
 *
177
 */
178
public class GeneralLabelingStrategy implements
179
ILabelingStrategy, Cloneable, CartographicSupport {
180
        
181
        private static final Logger logger = LoggerFactory.getLogger(
182
                        GeneralLabelingStrategy.class);
183
        
184
        public static IPlacementConstraints DefaultPointPlacementConstraints =
185
                        new PointPlacementConstraints();
186
        public static IPlacementConstraints DefaultLinePlacementConstraints =
187
                        new LinePlacementConstraints();
188
        public static IPlacementConstraints DefaultPolygonPlacementConstraints =
189
                        new PolygonPlacementConstraints();
190
        
191
        private static String[] NO_TEXT = { Messages.getText("text_field") };
192
        
193
        private static MultiShapePlacementConstraints DefaultMultiShapePlacementConstratints =
194
                        new MultiShapePlacementConstraints();
195
        
196
        private ILabelingMethod method;
197
        private IPlacementConstraints placementConstraints;
198
        protected FLyrVect layer;
199
        private IZoomConstraints zoomConstraints;
200
        private boolean allowOverlapping;
201
        private long parseTime;
202
        private int unit;
203
        private int referenceSystem;
204
        private double sizeAfter;
205
        private boolean printMode = false; /* indicate whether output is for a print product (PDF, PS, ...) */
206

    
207
        public void setLayer(FLayer layer) {
208
                FLyrVect l = (FLyrVect) layer;
209
                this.layer = l;
210
        }
211

    
212
        public ILabelingMethod getLabelingMethod() {
213
                return method;
214
        }
215

    
216
        public void setLabelingMethod(ILabelingMethod method) {
217
                this.method = method;
218
        }
219

    
220
        private class GeometryItem{
221
                public Geometry geom = null;
222
                public int weigh = 0;
223
                public double savedPerimeter;
224

    
225
                public GeometryItem(Geometry geom, int weigh){
226
                        this.geom = geom;
227
                        this.weigh = weigh;
228
                        this.savedPerimeter = 0;
229
                }
230
        }
231
        public void draw(BufferedImage mapImage, Graphics2D mapGraphics,
232
                        ViewPort viewPort,
233
                        Cancellable cancel,
234
                        double dpi) throws ReadException {
235
                
236
                int x = (int)viewPort.getOffset().getX();
237
                int y = (int)viewPort.getOffset().getY();
238
//                boolean bVisualFXEnabled = false; // if true, the user can see how the labeling is drawing up
239

    
240
                //offsets for page generation (PDF, PS, direct printing)
241
                int print_offset_x = x;
242
                int print_offset_y = y;
243
                if (printMode) {
244
                        //for printing, we never offset the labels themselves
245
                        x = 0;
246
                        y = 0;
247
                        printMode = false;
248
                }                
249

    
250
                TreeMap<String[], GeometryItem> labelsToPlace = null;
251
                parseTime =0;
252
//                long t1 = System.currentTimeMillis();
253
                String[] usedFields = getUsedFields();
254

    
255
                int notPlacedCount = 0;
256
                int placedCount = 0;
257

    
258
                /*
259
                 * Get the label placement solvers according the user's settings
260
                 */
261
                ILabelPlacement placement = PlacementManager.getPlacement(
262
                                getPlacementConstraints(), layer.getShapeType());
263

    
264

    
265
                BufferedImage targetBI;
266
                Graphics2D targetGr;
267

    
268

    
269
                /*
270
                 * get an ordered set of the LabelClasses up on the
271
                 * label priority
272
                 */
273
                ILabelClass[] lcs = method.getLabelClasses();
274
                TreeSet<ILabelClass> ts = new TreeSet<ILabelClass>(
275
                                new LabelClassComparatorByPriority());
276

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

    
279
                if (ts.size()==0) return;
280

    
281
                /*
282
                 * now we have an ordered set, it is only need to give a pass
283
                 * for each label class to render by priorities.
284
                 *
285
                 * If no priorities were defined, the following loop only executes
286
                 * once
287
                 */
288
                for (ILabelClass lc : ts) {
289
                        
290
                        FeatureSet fset =  method.getFeatureIteratorByLabelClass(layer, lc, viewPort, usedFields);
291

    
292
                        // duplicates treatment stuff
293
                        /* handle the duplicates mode */
294
                        int duplicateMode = getDuplicateLabelsMode();
295
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
296
                                // we need to register the labels already placed
297

    
298
                                labelsToPlace = new TreeMap<String[], GeometryItem>(
299
                                                new RemoveDuplicatesComparator());
300
                        }
301

    
302
                        boolean bLabelsReallocatable = !isAllowingOverlap();
303

    
304
                        BufferedImage overlapDetectImage = null;
305
                        Graphics2D overlapDetectGraphics = null;
306
                        if (bLabelsReallocatable) {
307
                                int width = viewPort.getImageWidth() + print_offset_x;
308

    
309
                                if(width<0){
310
                                        width = 1;
311
                                }
312
                                int height = viewPort.getImageHeight() + print_offset_y;
313
                                if(height<0){
314
                                        height = 1;
315
                                }
316
                                if (mapImage!=null)
317
                                        overlapDetectImage = new BufferedImage(
318
                                                        mapImage.getWidth()  + print_offset_x,
319
                                                        mapImage.getHeight()  + print_offset_y,
320
                                                        BufferedImage.TYPE_INT_ARGB
321
                                );
322
                                else
323
                                        overlapDetectImage = new BufferedImage(
324
                                                        viewPort.getImageWidth() + print_offset_x,
325
                                                        viewPort.getImageHeight() + print_offset_y,
326
                                                        BufferedImage.TYPE_INT_ARGB
327
                                        );
328

    
329
                                overlapDetectGraphics = overlapDetectImage.createGraphics();
330
                                overlapDetectGraphics.setRenderingHints(mapGraphics.getRenderingHints());
331
                        }
332
                        if (bLabelsReallocatable) {
333
                                targetBI = overlapDetectImage;
334
                                targetGr = overlapDetectGraphics;
335
                                targetGr.translate(-x, -y);
336
                        } else {
337
                                targetBI = mapImage;
338
                                targetGr = mapGraphics;
339
                        }
340

    
341
                        DisposableIterator diter = fset.fastIterator();
342
                        Feature featu = null;
343
                        Geometry geome = null;
344
                        while ( !cancel.isCanceled() && diter.hasNext()) {
345
                                
346
                                featu = (Feature) diter.next();
347
                                geome = featu.getDefaultGeometry();
348
                                if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
349
                                        continue;
350
                                }
351
                                        
352

    
353
                                if (!setupLabel(featu, lc, cancel,
354
                                                usedFields, viewPort, dpi, duplicateMode)) {
355
                                        continue;
356
                                }
357

    
358
                                String[] texts = lc.getTexts();
359
//                                System.out.println(texts[0]);
360
                                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
361
                                        // check if this text (so label) is already present in the map
362

    
363
                                        GeometryItem item = labelsToPlace.get(texts);
364
                                        if (item == null){
365
                                                item = new GeometryItem(geome, 0);
366
                                                labelsToPlace.put(texts, item);
367
                                        }
368
                                        if (item.geom != null) {
369
                                                
370
                                                notPlacedCount++;
371
                                                if (geome.getType() != Geometry.TYPES.POINT) {
372
                                                        // FJP: Cambiamos la uni?n por una comprobaci?n de longitud, por ejemplo.
373
                                                        // La geometr?a con mayor longitud del bounding box ser? la que etiquetamos.
374
                                                        // Ser? inexacto, pero m?s r?pido. Solo lo queremos para saber qu? entidad etiquetamos
375
                                                        // El problema con la uni?n es que para l?neas va muy mal (no sabes lo que te va
376
                                                        // a etiquetar, y para pol?gonos ser? muy lenta. De todas formas, habr?a que evitar
377
                                                        // la conversi?n al JTS.
378
//                                                        Geometry jtsGeom = item.geom.toJTSGeometry().union(geom.toJTSGeometry());
379
//                                                        if (jtsGeom instanceof LineString) {
380
//                                                                CoordinateSequence cs = ((LineString) (jtsGeom)).getCoordinateSequence();
381
//                                                                CoordinateSequences.reverse(cs);
382
//                                                                jtsGeom = new LineString(cs, null);
383
//                                                        }
384
//                                                        item.geom = FConverter.jts_to_igeometry(jtsGeom);
385

    
386
                                                        Envelope auxBox = geome.getEnvelope();
387
                                                        double perimeterAux = 2 * (auxBox.getLength(0)+auxBox.getLength(1)); 
388
                                                        if (perimeterAux > item.savedPerimeter) {
389
                                                                item.geom = geome; //FConverter.jts_to_igeometry(jtsGeom);
390
                                                                item.savedPerimeter = perimeterAux;
391
                                                        }
392
                                                } else {
393
                                                        int weigh = item.weigh;
394
                                                        Point pointFromLabel = item.geom.centroid();
395
                                                        Point pointGeome = geome.centroid();
396
                                                        item.geom = GeometryLocator.getGeometryManager().createPoint(
397
                                                                        (pointFromLabel.getX()*weigh + pointGeome.getX())/(weigh+1),
398
                                                                        (pointFromLabel.getY()*weigh + pointGeome.getY())/(weigh+1),
399
                                                                        Geometry.SUBTYPES.GEOM2D);
400
                                                }
401
                                        } else {
402
                                                item.geom = geome;
403
                                        }
404
                                        item.weigh++;
405
                                } else {
406
                                        // Check if size is a pixel
407
                                        if (isOnePoint(viewPort, geom)) {
408
                                                continue;
409
                                        }
410
//                                        lc.toCartographicSize(viewPort, dpi, null);
411

    
412
                                        drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, geom, cancel, dpi, bLabelsReallocatable);
413
                                        placedCount++;
414
                                }
415
                        }
416
                        
417
                        // ======= End iteration in feature set ====================
418
                        
419
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
420
                                Iterator<String[]> textsIt = labelsToPlace.keySet().iterator();
421
                                while ( !cancel.isCanceled() && textsIt.hasNext()) {
422
                                        notPlacedCount++;
423
                                        String[] texts = textsIt.next();
424

    
425
                                        GeometryItem item = labelsToPlace.get(texts);
426
                                        if(item != null){
427
                                                lc.setTexts(texts);
428
                                                // Check if size is a pixel
429
                                                if (isOnePoint(viewPort, item.geom)) {
430
                                                        continue;
431
                                                }
432

    
433
//                                                lc.toCartographicSize(viewPort, dpi, null);
434
                                                drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, item.geom, cancel, dpi, bLabelsReallocatable);
435
                                        }
436
                                }
437
                        }
438

    
439
                        if (bLabelsReallocatable) {
440
                                targetGr.translate(x, y);
441
                                if (mapImage!=null)
442
                                        ((Graphics2D)mapImage.getGraphics()).drawImage(overlapDetectImage, null, null);
443
                                else
444
                                        mapGraphics.drawImage(overlapDetectImage, null, null);
445
                        }
446

    
447

    
448
                } // big iteration
449
//                double totalTime = (System.currentTimeMillis()-t1);
450
//
451
//                int total = placedCount+notPlacedCount;
452
//
453
//                if (total>0)
454
//                Logger.getLogger(getClass()).info("Labeled layer '"+layer.getName()+
455
//                "' "+totalTime/1000D+" seconds. "+placedCount+"/"+total+
456
//                " labels placed ("+NumberFormat.getInstance().
457
//                format(100*placedCount/(double) total)+"%)");
458
//
459
//                if (cancel.isCanceled()) {
460
//                        Logger.getLogger(getClass()).info("Layer labeling canceled: '"+
461
//                                        layer.getName()+"'");
462
//                } else {
463
//                        Logger.getLogger(getClass()).info("Total labels parse time = "+
464
//                                        parseTime+" ("+NumberFormat.getInstance().
465
//                                        format(parseTime*100/totalTime)+"%)");
466
//                }
467

    
468
        }
469
        
470
        
471
        private void drawLabelInGeom(
472
                        BufferedImage targetBI, Graphics2D targetGr, ILabelClass lc,
473
                        ILabelPlacement placement, ViewPort viewPort, Geometry geom, Cancellable cancel,
474
                        double dpi, boolean bLabelsReallocatable) {
475

    
476
                lc.toCartographicSize(viewPort, dpi, null);
477
                ArrayList<LabelLocationMetrics> llm = null;
478
                llm = placement.guess(
479
                                lc,
480
                                geom,
481
                                getPlacementConstraints(),
482
                                0,
483
                                cancel,viewPort);
484

    
485
                setReferenceSystem(lc.getReferenceSystem());
486
                setUnit(lc.getUnit());
487

    
488
                /*
489
                 *  Esto provoca errores en el calculo del tama?o de la LabelClass
490
                 * as? que se ha diferido el calculo del tama?o con que deber?a
491
                 * dibujarse el texto justo hasta el momento en que se dibuja ?ste,
492
                 * en el metodo draw de la LabelClass.
493
                 *
494
                 * FIXME: Mantengo el c?digo viejo comentarizado para enfatizar
495
                 * este comentario. Eliminar cuando se asuma que es correcto el cambio.
496
                 *
497
                 */
498

    
499
//                double sizeBefore = lc.getTextSymbol().getFont().getSize();
500

    
501
//                sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
502
//                                sizeBefore,
503
//                                viewPort,
504
//                                MapContext.getScreenDPI());
505

    
506

    
507
                /*
508
                 * search if there is room left by the previous and
509
                 * with more priority labels, then check the current
510
                 * level
511
                 */
512
//                if (
513
                lookupAndPlaceLabel(targetBI, targetGr, llm,
514
                                placement, lc, geom, viewPort, cancel,        bLabelsReallocatable);  //{
515

    
516
//                        lc.getTextSymbol().setFontSize(sizeBefore);
517
//                }
518
//                lc.toCartographicSize(viewPort, dpi, null);
519

    
520
        }
521

    
522
        private int getDuplicateLabelsMode() {
523
                if (getPlacementConstraints() == null) {
524
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
525
                }
526
                return getPlacementConstraints().getDuplicateLabelsMode();
527
        }
528

    
529
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
530
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
531
                        ILabelClass lc, Geometry geom,        ViewPort viewPort,
532
                        Cancellable cancel, boolean bLabelsReallocatable) {
533
                
534
                int i;
535
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
536
                        LabelLocationMetrics labelMetrics = llm.get(i);
537

    
538
                        IPlacementConstraints pc = getPlacementConstraints();
539
                        if(pc instanceof MultiShapePlacementConstraints) {
540
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints)pc;
541
                                
542
                                GeometryManager gm = GeometryLocator.getGeometryManager();
543
                                GeometryType line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
544
                                GeometryType polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
545
                                GeometryType geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
546

    
547
                                if (geom_gt.getType() == TYPES.POINT ||
548
                                                geom_gt.getType() == TYPES.MULTIPOINT) {
549
                                        pc = mpc.getPointConstraints();
550
                                } else {
551
                                        if (geom_gt.isTypeOf(TYPES.CURVE) ||
552
                                                        geom_gt.getType() == TYPES.MULTICURVE) {
553
                                                pc = mpc.getLineConstraints();
554
                                        } else {
555
                                                if (geom_gt.isTypeOf(TYPES.SURFACE) ||
556
                                                                geom_gt.getType() == TYPES.MULTISURFACE) {
557
                                                        pc = mpc.getPolygonConstraints();
558
                                                }
559
                                        }
560
                                }
561
                        }
562

    
563
                        /*
564
                         * Ver comentario en el metodo drawLabelInGeom
565
                         */
566
//                        lc.getTextSymbol().setFontSize(sizeAfter);// * FConstant.FONT_HEIGHT_SCALE_FACTOR);
567
                        if (bLabelsReallocatable) {
568
                                if (!isOverlapping(bi, lc.getShape(labelMetrics))) {
569

    
570
                                        if(!pc.isFollowingLine()){
571
                                                lc.draw(g, labelMetrics, geom);
572
                                        } else{
573
                                                SmartTextSymbolLabelClass smsLc = new SmartTextSymbolLabelClass();
574
                                                SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
575

    
576
                                                double sizeBefore = lc.getTextSymbol().getFont().getSize();
577
                                                double sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
578
                                                                sizeBefore,
579
                                                                viewPort,
580
                                                                MapContext.getScreenDPI());
581
                                                sms.setFontSize(sizeAfter);
582

    
583
                                                smsLc.setTextSymbol(sms);
584
                                                geom.transform(viewPort.getAffineTransform());
585
                                                smsLc.draw(g, null, (FShape) geom.getInternalShape());
586
                                                sms.setFontSize(sizeBefore);
587

    
588
                                        }
589
                                        return true;
590
                                }
591
                        } else {
592
                                if(!pc.isFollowingLine()){
593
                                        lc.draw(g, labelMetrics, null);
594
                                }
595
                                else{
596
                                        SmartTextSymbolLabelClass smsLc = new SmartTextSymbolLabelClass();
597
                                        SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
598

    
599
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
600
                                        double sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
601
                                                        sizeBefore,
602
                                                        viewPort,
603
                                                        MapContext.getScreenDPI());
604
                                        sms.setFontSize(sizeAfter);
605

    
606
                                        smsLc.setTextSymbol(sms);
607
                                        geom.transform(viewPort.getAffineTransform());
608
                                        smsLc.draw(g, null, (FShape) geom.getInternalShape());
609

    
610
                                        sms.setFontSize(sizeBefore);
611
                                }
612
                                return true;
613
                        }
614
                }
615
                return false;
616
        }
617

    
618
        /**
619
         * Divide una cadena de caracteres por el caracter dos puntos siempre que no est? entre comillas.
620
         *
621
         * @param str
622
         *            Cadena de caracteres
623
         *
624
         * @return String[]
625
         *
626
         */
627
        private String[] divideExpression(String str){
628
                ArrayList<String> r = new ArrayList<String>();
629
                boolean inQuotationMarks = false;
630
                int lastIndex = 0;
631
                for(int i=0; i<str.length(); i++){
632
                        if(str.substring(i, i+1).compareTo("\"")==0){
633
                                inQuotationMarks = !inQuotationMarks;
634
                                continue;
635
                        }
636
                        if(str.substring(i, i+1).compareTo(":")==0 && !inQuotationMarks){
637
                                if(lastIndex < i){
638
                                        r.add(str.substring(lastIndex, i));
639
                                }
640
                                lastIndex = i+1;
641
                        }
642
                }
643
                if(lastIndex < str.length()-1){
644
                        r.add(str.substring(lastIndex));
645
                }
646
                String[] result = new String[r.size()];
647
                r.toArray(result);
648
                return result;
649
        }
650

    
651
        /**
652
         * Compute the texts to show in the label and store them in LabelClass.
653
         */
654
        @SuppressWarnings("unchecked")
655
        private boolean setupLabel(Feature featu, ILabelClass lc,
656
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
657
                        double dpi, int duplicateMode){//, TreeSet<?> placedLabels) {
658

    
659
//                Value[] vv = feat.getAttributes();
660
                String expr = lc.getStringLabelExpression();
661

    
662
                long pt1 = System.currentTimeMillis();
663
                String[] texts = NO_TEXT;
664
                ArrayList<String> preTexts = new ArrayList<String>();
665
//                String[] texts = {PluginServices.getText(this, "text_field")};
666
                try {
667

    
668
                        for (int i = 0; !cancel.isCanceled() && i < usedFields.length; i++) {
669
                                try {
670
                                        symbol_table.put(usedFields[i], featu.get(usedFields[i]));
671
                                } catch (Exception e) {
672
                                        logger.error("While geatting feature values.", e);
673
                                }
674
                        }
675

    
676
                        if (expr != null) {
677

    
678
                                if(expr.equals("") || expr.equals(
679
                                                LabelExpressionParser.tokenFor(LabelExpressionParser.EOEXPR)))
680
                                        expr = texts[0];
681

    
682
                                /*
683
                                 * FIXME: Esto es un parche.
684
                                 * Parece que el LabelExpresionParser no es capaz de
685
                                 * de dividir convenientemente una expression compuesta por varias expresiones
686
                                 * separadas por ":".
687
                                 * Habr?a que arreglar (con tiempo) el evaluador, pero,
688
                                 * de momento, rodeamos el problema.
689
                                 */
690
                                String[] multiexpr = divideExpression(expr);
691
                                for(int i=0; i<multiexpr.length; i++){
692
                                        expr = multiexpr[i];
693
                                        if(!expr.endsWith(";")) expr += ";";
694
                                        // parse (if it hasn't been parsed yet) and evaluate the expression
695
                                        Expression evaluator = getEvaluator(expr);
696
                                        Object labelContents = evaluator.evaluate();
697
                                        if (labelContents!=null) {
698
                                                if (String[].class.equals(labelContents.getClass())) {
699
                                                        for(int j=0; j<((String[]) labelContents).length;j++){
700
                                                                preTexts.add(((String[])labelContents)[j]);
701
                                                        }
702
                                                } else {
703
                                                        preTexts.add(labelContents.toString());
704
                                                }
705
                                        }
706
                                }
707
                                texts = new String[preTexts.size()];
708
                                preTexts.toArray(texts);
709
                                parseTime += System.currentTimeMillis()-pt1;
710
                        }
711
                        lc.setTexts(texts);
712

    
713
                } catch (ExpressionException e) {
714
                        logger.error("While setting up label", e);
715
                        return false;
716
                }
717
                return true;
718
        }
719
        
720
        private Hashtable<String, Object> symbol_table = new Hashtable<String, Object>();
721
        private Hashtable<String, Expression> evaluators = new Hashtable<String, Expression>();
722

    
723
        private Expression getEvaluator(String strExpr) {
724
                Expression expr = evaluators.get(strExpr);
725
                if (expr == null) {
726
                        LabelExpressionParser p = new LabelExpressionParser(
727
                                        new StringReader(strExpr),symbol_table);
728

    
729
                        try {
730
                                p.LabelExpression();
731
                        } catch (ParseException e) {
732
                                e.printStackTrace();
733
                        }
734
                        expr = (Expression) p.getStack().pop();
735
                        evaluators.put(strExpr, expr);
736
                }
737
                return expr;
738
        }
739

    
740
        private boolean isOverlapping(BufferedImage bi, FShape labelShape) {
741
                if (labelShape==null)
742
                        return false;
743
                Rectangle2D rPixels = labelShape.getBounds2D();
744
                for (int i= (int) rPixels.getX(); i<=rPixels.getMaxX(); i++){
745
                        for (int j= (int) rPixels.getY(); j<=rPixels.getMaxY(); j++){
746

    
747
                                if (!labelShape.contains(i, j) // contains seems to don't detect points placed in the rectangle boundaries
748
                                                && !labelShape.intersects(i, j, i, j)) {
749
                                        continue;
750
                                }
751

    
752
                                if (i<0 || j<0) {
753
                                        continue;
754
                                }
755

    
756
                                if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
757
                                        continue;
758
                                }
759

    
760
                                if (bi.getRGB(i,j)!=0){
761
                                        return true;
762
                                }
763
                        }
764
                }
765
                return false;
766
        }
767

    
768
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
769
                
770
                boolean onePoint = false;
771
                int shapeType = geom.getType();
772
                
773
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
774

    
775
                        Envelope env = geom.getEnvelope();
776
                        double dist1Pixel = viewPort.getDist1pixel();
777
                        onePoint = (env.getLength(0) <= dist1Pixel
778
                                        && env.getLength(1) <= dist1Pixel);
779
                }
780
                return onePoint;
781
        }
782

    
783
        public String getClassName() {
784
                return getClass().getName();
785
        }
786

    
787
        public XMLEntity getXMLEntity() {
788
                XMLEntity xml = new XMLEntity();
789
                xml.putProperty("labelingStrategy", "labelingStrategy");
790
                xml.putProperty("className", getClassName());
791
                xml.putProperty("allowsOverlapping", allowOverlapping);
792
//                xml.putProperty("minScaleView", minScaleView);
793
//                xml.putProperty("maxScaleView", maxScaleView);
794

    
795
                if (method!=null) {
796
                        XMLEntity methodEntity = method.getXMLEntity();
797
                        methodEntity.putProperty("id", "LabelingMethod");
798
                        xml.addChild(methodEntity);
799
                }
800

    
801
                if (placementConstraints != null) {
802
                        XMLEntity pcEntity = placementConstraints.getXMLEntity();
803
                        pcEntity.putProperty("id", "PlacementConstraints");
804
                        xml.addChild(pcEntity);
805
                }
806

    
807
                if (zoomConstraints != null) {
808
                        XMLEntity zcEntity = zoomConstraints.getXMLEntity();
809
                        zcEntity.putProperty("id", "ZoomConstraints");
810
                        xml.addChild(zcEntity);
811
                }
812
                return xml;
813
        }
814

    
815
        public void setXMLEntity(XMLEntity xml) {
816
                XMLEntity aux = xml.firstChild("id", "LabelingMethod");
817

    
818
                // overlapping mode
819
                if (xml.contains("allowsOverlapping")) {
820
                        allowOverlapping = xml.getBooleanProperty("allowsOverlapping");
821
                }
822

    
823

    
824
                if (aux != null) {
825
                        method = LabelingFactory.createMethodFromXML(aux);
826
                }
827

    
828
                aux = xml.firstChild("id", "PlacementConstraints");
829
                if (aux != null) {
830
                        placementConstraints = LabelingFactory.createPlacementConstraintsFromXML(aux);
831
                }
832

    
833
                aux = xml.firstChild("id", "ZoomConstraints");
834
                if (aux != null) {
835
                        zoomConstraints = LabelingFactory.createZoomConstraintsFromXML(aux);
836
                }
837
        }
838

    
839
        public boolean isAllowingOverlap() {
840
                return allowOverlapping;
841
        }
842

    
843
        public void setAllowOverlapping(boolean allowOverlapping) {
844
                this.allowOverlapping = allowOverlapping;
845
        }
846

    
847
        public IPlacementConstraints getPlacementConstraints() {
848
                if (placementConstraints != null)
849
                        return placementConstraints;
850

    
851
                try {
852
                        switch (layer.getShapeType() % FShape.Z) {
853
                        case FShape.POINT:
854
                        case FShape.MULTIPOINT:
855
                                return DefaultPointPlacementConstraints;
856
                        case FShape.LINE:
857
                                return DefaultLinePlacementConstraints;
858
                        case FShape.POLYGON:
859
                                return DefaultPolygonPlacementConstraints;
860
                        case FShape.MULTI:
861
                                DefaultMultiShapePlacementConstratints.setPointConstraints(DefaultPointPlacementConstraints);
862
                                DefaultMultiShapePlacementConstratints.setLineConstraints(DefaultLinePlacementConstraints);
863
                                DefaultMultiShapePlacementConstratints.setPolygonConstraints(DefaultPolygonPlacementConstraints);
864
                                return DefaultMultiShapePlacementConstratints;
865
                        }
866

    
867
                } catch (ReadDriverException e) {
868

    
869
                }
870
                return null;
871
        }
872

    
873
        public void setPlacementConstraints(IPlacementConstraints constraints) {
874
                this.placementConstraints = constraints;
875
        }
876

    
877
        public IZoomConstraints getZoomConstraints() {
878
                return zoomConstraints;
879
        }
880

    
881
        public void setZoomConstraints(IZoomConstraints constraints) {
882
                this.zoomConstraints = constraints;
883
        }
884

    
885
        public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel, PrintRequestAttributeSet properties) throws ReadDriverException {
886
                double dpi = 100;
887

    
888
                PrintQuality resolution=(PrintQuality)properties.get(PrintQuality.class);
889
                if (resolution.equals(PrintQuality.NORMAL)){
890
                        dpi = 300;
891
                } else if (resolution.equals(PrintQuality.HIGH)){
892
                        dpi = 600;
893
                } else if (resolution.equals(PrintQuality.DRAFT)){
894
                        dpi = 72;
895
                }
896

    
897
                viewPort.setOffset(new Point2D.Double(0,0));        
898
                
899
                /* signal printing output */
900
                printMode = true;
901

    
902
                draw(null,g,viewPort,cancel,dpi);
903
        }
904

    
905
        public String[] getUsedFields() {
906
                LabelClass[] lcs = method.getLabelClasses();
907
                ArrayList<String> fieldNames = new ArrayList<String>();
908
                for (int i = 0; i < lcs.length; i++) {
909
                        if(lcs[i].getLabelExpressions() != null){
910
                                for (int j = 0; j < lcs[i].getLabelExpressions().length; j++) {
911
                                        String expr = lcs[i].getLabelExpressions()[j];
912
                                        int start;
913
                                        while (expr != null &&
914
                                                        (start = expr.indexOf("[")) != -1) {
915
                                                int end = expr.indexOf("]");
916
                                                String field = expr.substring(start+1, end).trim();
917
                                                if (!fieldNames.contains(field))
918
                                                        fieldNames.add(field);
919
                                                expr = expr.substring(end+1, expr.length());
920
                                        }
921
                                }
922
                        }
923
                }
924
                return fieldNames.toArray(new String[fieldNames.size()]);
925
        }
926

    
927

    
928
        public boolean shouldDrawLabels(double scale) {
929
                double minScaleView = -1;
930
                double maxScaleView = -1;
931

    
932
                if (zoomConstraints != null) {
933
                        minScaleView = zoomConstraints.getMinScale();
934
                        maxScaleView = zoomConstraints.getMaxScale();
935
                }
936

    
937
                if (minScaleView == -1 && maxScaleView == -1) {
938
                        // parameters not set, so the layer decides.
939
                        return layer.isWithinScale(scale);
940
                }
941

    
942
                if (minScaleView >= scale) {
943
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
944
                }
945

    
946
                return false;
947
        }
948

    
949
        public void setUnit(int unitIndex) {
950
                unit = unitIndex;
951

    
952
        }
953

    
954
        public int getUnit() {
955
                return unit;
956
        }
957

    
958
        public int getReferenceSystem() {
959
                return referenceSystem;
960
        }
961

    
962
        public void setReferenceSystem(int referenceSystem) {
963
                this.referenceSystem = referenceSystem;
964
        }
965

    
966
        public double toCartographicSize(ViewPort viewPort, double dpi, FShape shp) {
967
                // TODO Auto-generated method stub
968
                return 0;
969
        }
970

    
971
        public void setCartographicSize(double cartographicSize, FShape shp) {
972
                // TODO Auto-generated method stub
973

    
974
        }
975

    
976
        public double getCartographicSize(ViewPort viewPort, double dpi, FShape shp) {
977
                // TODO Auto-generated method stub
978
                return 0;
979
        }
980

    
981
}