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

History | View | Annotate | Download (32.2 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.geom.Point2D;
112
import java.awt.image.BufferedImage;
113
import java.util.ArrayList;
114
import java.util.Iterator;
115
import java.util.List;
116
import java.util.TreeMap;
117
import java.util.TreeSet;
118

    
119
import org.slf4j.Logger;
120
import org.slf4j.LoggerFactory;
121

    
122
import org.gvsig.compat.print.PrintAttributes;
123
import org.gvsig.fmap.dal.exception.DataException;
124
import org.gvsig.fmap.dal.exception.ReadException;
125
import org.gvsig.fmap.dal.feature.Feature;
126
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
127
import org.gvsig.fmap.dal.feature.FeatureSet;
128
import org.gvsig.fmap.geom.Geometry;
129
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
130
import org.gvsig.fmap.geom.Geometry.TYPES;
131
import org.gvsig.fmap.geom.GeometryException;
132
import org.gvsig.fmap.geom.GeometryLocator;
133
import org.gvsig.fmap.geom.GeometryManager;
134
import org.gvsig.fmap.geom.aggregate.MultiPrimitive;
135
import org.gvsig.fmap.geom.operation.GeometryOperationException;
136
import org.gvsig.fmap.geom.operation.GeometryOperationNotSupportedException;
137
import org.gvsig.fmap.geom.primitive.Envelope;
138
import org.gvsig.fmap.geom.primitive.Point;
139
import org.gvsig.fmap.geom.type.GeometryType;
140
import org.gvsig.fmap.mapcontext.ViewPort;
141
import org.gvsig.fmap.mapcontext.layers.FLayer;
142
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
143
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelClass;
144
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingMethod;
145
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingStrategy;
146
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IPlacementConstraints;
147
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IZoomConstraints;
148
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
149
import org.gvsig.i18n.Messages;
150
import org.gvsig.labeling.lang.LabelClassUtils;
151
import org.gvsig.labeling.placements.ILabelPlacement;
152
import org.gvsig.labeling.placements.LinePlacementConstraints;
153
import org.gvsig.labeling.placements.MultiShapePlacementConstraints;
154
import org.gvsig.labeling.placements.PlacementManager;
155
import org.gvsig.labeling.placements.PointPlacementConstraints;
156
import org.gvsig.labeling.placements.PolygonPlacementConstraints;
157
import org.gvsig.labeling.placements.RemoveDuplicatesComparator;
158
import org.gvsig.labeling.symbol.SmartTextSymbol;
159
import org.gvsig.labeling.symbol.SymbolUtils;
160
import org.gvsig.symbology.SymbologyLocator;
161
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.styling.LabelLocationMetrics;
162
import org.gvsig.tools.ToolsLocator;
163
import org.gvsig.tools.dispose.DisposableIterator;
164
import org.gvsig.tools.dynobject.DynStruct;
165
import org.gvsig.tools.persistence.PersistenceManager;
166
import org.gvsig.tools.persistence.PersistentState;
167
import org.gvsig.tools.persistence.exception.PersistenceException;
168
import org.gvsig.tools.task.Cancellable;
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 ILabelingStrategy, Cloneable,
179
                CartographicSupport {
180

    
181
        private static final Logger logger = LoggerFactory
182
                        .getLogger(GeneralLabelingStrategy.class);
183

    
184
        public static final String GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME = "GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME";
185

    
186
        public static PointPlacementConstraints DefaultPointPlacementConstraints = new PointPlacementConstraints();
187
        public static LinePlacementConstraints DefaultLinePlacementConstraints = new LinePlacementConstraints();
188
        public static PolygonPlacementConstraints DefaultPolygonPlacementConstraints = new PolygonPlacementConstraints();
189

    
190
        private static String[] NO_TEXT = { Messages.getText("text_field") };
191

    
192
        private static MultiShapePlacementConstraints DefaultMultiShapePlacementConstratints = new MultiShapePlacementConstraints();
193

    
194
        private ILabelingMethod method;
195
        private IPlacementConstraints placementConstraints;
196
        private IZoomConstraints zoomConstraints;
197

    
198
        private boolean allowOverlapping;
199

    
200
        protected FLyrVect layer;
201

    
202
        // private long parseTime;
203
        private int unit;
204
        private int referenceSystem;
205
        // private double sizeAfter;
206
        private boolean printMode = false; /*
207
                                                                                 * indicate whether output is for a
208
                                                                                 * print product (PDF, PS, ...)
209
                                                                                 */
210

    
211
        private List<Geometry> drawnGeometryLabels;
212

    
213
        public GeneralLabelingStrategy() {
214
                method = SymbologyLocator.getSymbologyManager()
215
                                .createDefaultLabelingMethod();
216
        }
217

    
218
        public void setLayer(FLayer layer) {
219
                FLyrVect l = (FLyrVect) layer;
220
                this.layer = l;
221
        }
222

    
223
        public ILabelingMethod getLabelingMethod() {
224
                return method;
225
        }
226

    
227
        public void setLabelingMethod(ILabelingMethod method) {
228
                this.method = method;
229
        }
230

    
231
        private class GeometryItem {
232
                public Geometry geom = null;
233
                public int weigh = 0;
234
                public double savedPerimeter;
235

    
236
                public GeometryItem(Geometry geom, int weigh) {
237
                        this.geom = geom;
238
                        this.weigh = weigh;
239
                        this.savedPerimeter = 0;
240
                }
241
        }
242

    
243
    public void draw(BufferedImage mapImage, Graphics2D mapGraphics, double scale, ViewPort viewPort,
244
        Cancellable cancel, double dpi) throws ReadException {
245

    
246
        drawnGeometryLabels = new ArrayList<Geometry>(1000);
247

    
248
        int x = (int) viewPort.getOffset().getX();
249
        int y = (int) viewPort.getOffset().getY();
250

    
251
        // offsets for page generation (PDF, PS, direct printing)
252
        int print_offset_x = x;
253
        int print_offset_y = y;
254
        if (printMode) {
255
            // for printing, we never offset the labels themselves
256
            x = 0;
257
            y = 0;
258
            printMode = false;
259
        }
260

    
261
        TreeMap<String[], GeometryItem> labelsToPlace = null;
262

    
263
        String[] usedFields = getUsedFields();
264

    
265
        int notPlacedCount = 0;
266
        int placedCount = 0;
267

    
268
        /*
269
         * Get the label placement solvers according the user's settings
270
         */
271
        ILabelPlacement placement = PlacementManager.getPlacement(getPlacementConstraints(), layer.getShapeType());
272

    
273
        BufferedImage targetBI;
274
        Graphics2D targetGr;
275

    
276
        /*
277
         * get an ordered set of the LabelClasses up on the label priority
278
         */
279
        ILabelClass[] lcs = method.getLabelClasses();
280
        TreeSet<ILabelClass> ts = new TreeSet<ILabelClass>(new LabelClassComparatorByPriority());
281

    
282
        for (int i = 0; i < lcs.length; i++)
283
            ts.add(lcs[i]);
284

    
285
        if (ts.size() == 0)
286
            return;
287

    
288
        /*
289
         * now we have an ordered set, it is only need to give a pass for each
290
         * label class to render by priorities.
291
         *
292
         * If no priorities were defined, the following loop only executes once
293
         */
294
        for (ILabelClass lc : ts) {
295

    
296
            if (!lc.isVisible(scale)) {
297
                /*
298
                 * Avoid non-visible labels
299
                 */
300
                continue;
301
            }
302

    
303
            FeatureSet fset = null;
304
            DisposableIterator diter = null;
305
            try {
306

    
307
                try {
308
                    fset = method.getFeatureIteratorByLabelClass(layer, lc, viewPort, usedFields);
309
                } catch (DataException e) {
310
                    throw new ReadException(layer.getFeatureStore().getProviderName(), e);
311
                }
312

    
313
                // duplicates treatment stuff
314
                /* handle the duplicates mode */
315
                int duplicateMode = getDuplicateLabelsMode();
316
                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
317
                    // we need to register the labels already placed
318

    
319
                    labelsToPlace = new TreeMap<String[], GeometryItem>(new RemoveDuplicatesComparator());
320
                }
321

    
322
                boolean bLabelsReallocatable = !isAllowingOverlap();
323

    
324
                BufferedImage overlapDetectImage = null;
325
                Graphics2D overlapDetectGraphics = null;
326
                if (bLabelsReallocatable) {
327
                    int width = viewPort.getImageWidth() + print_offset_x;
328

    
329
                    if (width < 0) {
330
                        width = 1;
331
                    }
332
                    int height = viewPort.getImageHeight() + print_offset_y;
333
                    if (height < 0) {
334
                        height = 1;
335
                    }
336
                    if (mapImage != null)
337
                        overlapDetectImage =
338
                            new BufferedImage(mapImage.getWidth() + print_offset_x, mapImage.getHeight()
339
                                + print_offset_y, BufferedImage.TYPE_INT_ARGB);
340
                    else
341
                        overlapDetectImage =
342
                            new BufferedImage(viewPort.getImageWidth() + print_offset_x, viewPort.getImageHeight()
343
                                + print_offset_y, BufferedImage.TYPE_INT_ARGB);
344

    
345
                    overlapDetectGraphics = overlapDetectImage.createGraphics();
346
                    overlapDetectGraphics.setRenderingHints(mapGraphics.getRenderingHints());
347
                }
348
                if (bLabelsReallocatable) {
349
                    targetBI = overlapDetectImage;
350
                    targetGr = overlapDetectGraphics;
351
                    targetGr.translate(-x, -y);
352
                } else {
353
                    targetBI = mapImage;
354
                    targetGr = mapGraphics;
355
                }
356

    
357
                try {
358
                    diter = fset.fastIterator();
359
                } catch (DataException e) {
360
                    throw new ReadException(layer.getFeatureStore().getProviderName(), e);
361
                }
362
                Feature featu = null;
363
                Geometry geome = null;
364

    
365
                while (!cancel.isCanceled() && diter.hasNext()) {
366

    
367
                    featu = (Feature) diter.next();
368
                    geome = featu.getDefaultGeometry();
369
                    if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
370
                        continue;
371
                    }
372

    
373
                    if (!setupLabel(featu, lc, cancel, usedFields, viewPort, dpi, duplicateMode)) {
374
                        continue;
375
                    }
376

    
377
                    String[] texts = lc.getTexts();
378

    
379
                    if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
380
                        // check if this text (so label) is already present in
381
                        // the map
382

    
383
                        GeometryItem item = labelsToPlace.get(texts);
384
                        if (item == null) {
385
                            item = new GeometryItem(geome, 0);
386
                            labelsToPlace.put(texts, item);
387
                        }
388
                        if (item.geom != null) {
389

    
390
                            notPlacedCount++;
391
                            if (geome.getType() != Geometry.TYPES.POINT) {
392

    
393
                                Envelope auxBox = geome.getEnvelope();
394
                                double perimeterAux = 2 * (auxBox.getLength(0) + auxBox.getLength(1));
395
                                if (perimeterAux > item.savedPerimeter) {
396
                                    item.geom = geome; // FConverter.jts_to_igeometry(jtsGeom);
397
                                    item.savedPerimeter = perimeterAux;
398
                                }
399
                            } else {
400
                                int weigh = item.weigh;
401

    
402
                                try {
403
                                    Point pointFromLabel = item.geom.centroid();
404
                                    Point pointGeome = geome.centroid();
405
                                    item.geom =
406
                                        GeometryLocator.getGeometryManager().createPoint(
407
                                            (pointFromLabel.getX() * weigh + pointGeome.getX()) / (weigh + 1),
408
                                            (pointFromLabel.getY() * weigh + pointGeome.getY()) / (weigh + 1),
409
                                            Geometry.SUBTYPES.GEOM2D);
410
                                } catch (Exception ex) {
411
                                    throw new ReadException(layer.getFeatureStore().getProviderName(), ex);
412
                                }
413

    
414
                            }
415
                        } else {
416
                            item.geom = geome;
417
                        }
418
                        item.weigh++;
419
                    } else {
420
                        // Check if size is a pixel
421
                        if (isOnePoint(viewPort, geome)) {
422
                            continue;
423
                        }
424

    
425
                        List<Geometry> geome_parts = new ArrayList<Geometry>();
426
                        if (duplicateMode == IPlacementConstraints.ONE_LABEL_PER_FEATURE_PART) {
427
                            geome_parts = getGeometryParts(geome);
428
                        } else {
429
                            geome_parts.add(geome);
430
                        }
431

    
432
                        try {
433
                            int n = geome_parts.size();
434
                            for (int k = 0; k < n; k++) {
435
                                drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, geome_parts.get(k),
436
                                    cancel, dpi, bLabelsReallocatable);
437
                            }
438
                        } catch (GeometryException e) {
439
                            throw new ReadException(layer.getFeatureStore().getProviderName(), e);
440
                        }
441

    
442
                        placedCount++;
443
                    }
444
                }
445

    
446
                // ======= End iteration in feature set ====================
447

    
448
                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
449
                    Iterator<String[]> textsIt = labelsToPlace.keySet().iterator();
450
                    while (!cancel.isCanceled() && textsIt.hasNext()) {
451
                        notPlacedCount++;
452
                        String[] texts = textsIt.next();
453

    
454
                        GeometryItem item = labelsToPlace.get(texts);
455
                        if (item != null) {
456
                            lc.setTexts(texts);
457
                            // Check if size is a pixel
458
                            if (isOnePoint(viewPort, item.geom)) {
459
                                continue;
460
                            }
461

    
462
                            try {
463
                                drawLabelInGeom(targetBI, targetGr, lc, placement, viewPort, item.geom, cancel, dpi,
464
                                    bLabelsReallocatable);
465
                            } catch (GeometryException e) {
466
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
467
                            }
468
                        }
469
                    }
470
                }
471

    
472
                if (bLabelsReallocatable) {
473
                    targetGr.translate(x, y);
474
                    if (mapImage != null) {
475
                        mapGraphics.drawImage(overlapDetectImage, null, null);
476
                    } else {
477
                        mapGraphics.drawImage(overlapDetectImage, null, null);
478
                    }
479
                }
480

    
481
            } finally {
482
                if (diter != null) {
483
                    diter.dispose();
484
                }
485
                if (fset != null) {
486
                    fset.dispose();
487
                }
488
            }
489
        } // big iteration
490

    
491
    }
492

    
493
        private List<Geometry> getGeometryParts(Geometry ge) {
494

    
495
                List<Geometry> resp = new ArrayList<Geometry>();
496
                if (ge != null) {
497
                        if (ge instanceof MultiPrimitive) {
498
                                MultiPrimitive mp = (MultiPrimitive) ge;
499
                                int n = mp.getPrimitivesNumber();
500
                                for (int i = 0; i < n; i++) {
501
                                        resp.add(mp.getPrimitiveAt(i));
502
                                }
503
                        } else {
504
                                resp.add(ge);
505
                        }
506
                }
507
                return resp;
508
        }
509

    
510
        private void drawLabelInGeom(BufferedImage targetBI, Graphics2D targetGr,
511
                        ILabelClass lc, ILabelPlacement placement, ViewPort viewPort,
512
                        Geometry geom, Cancellable cancel, double dpi,
513
                        boolean bLabelsReallocatable) throws GeometryException {
514

    
515
                lc.toCartographicSize(viewPort, dpi, null);
516
                ArrayList<LabelLocationMetrics> llm = null;
517
                llm = placement.guess(lc, geom, getPlacementConstraints(), 0, cancel,
518
                                viewPort);
519

    
520
                setReferenceSystem(lc.getReferenceSystem());
521
                setUnit(lc.getUnit());
522

    
523
                /*
524
                 * search if there is room left by the previous and with more priority
525
                 * labels, then check the current level
526
                 */
527
                lookupAndPlaceLabel(targetBI, targetGr, llm, placement, lc, geom,
528
                                viewPort, cancel, bLabelsReallocatable);
529

    
530
        }
531

    
532
        private int getDuplicateLabelsMode() {
533
                if (getPlacementConstraints() == null) {
534
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
535
                }
536
                return getPlacementConstraints().getDuplicateLabelsMode();
537
        }
538

    
539
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
540
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
541
                        ILabelClass lc, Geometry geom, ViewPort viewPort,
542
                        Cancellable cancel, boolean bLabelsReallocatable)
543
                        throws GeometryException {
544

    
545
            GeometryManager gm = GeometryLocator.getGeometryManager();
546
                int i;
547
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
548
                        LabelLocationMetrics labelMetrics = llm.get(i);
549

    
550
                        IPlacementConstraints pc = getPlacementConstraints();
551
                        if (pc instanceof MultiShapePlacementConstraints) {
552
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints) pc;
553

    
554
                                GeometryType geom_gt = null;
555

    
556
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
557

    
558
                                if (geom_gt.getType() == TYPES.POINT
559
                                                || geom_gt.getType() == TYPES.MULTIPOINT) {
560
                                        pc = mpc.getPointConstraints();
561
                                } else {
562
                                        if (geom_gt.isTypeOf(TYPES.CURVE)
563
                                                        || geom_gt.getType() == TYPES.MULTICURVE) {
564
                                                pc = mpc.getLineConstraints();
565
                                        } else {
566
                                                if (geom_gt.isTypeOf(TYPES.SURFACE)
567
                                                                || geom_gt.getType() == TYPES.MULTISURFACE) {
568
                                                        pc = mpc.getPolygonConstraints();
569
                                                }
570
                                        }
571
                                }
572
                        }
573

    
574
                        /*
575
                         * Ver comentario en el metodo drawLabelInGeom
576
                         */
577
                        if (bLabelsReallocatable) {
578

    
579
                                Geometry aux_geom = null;
580
                                aux_geom = lc.getShape(labelMetrics);
581

    
582
                                if (!isOverlapping(bi, aux_geom)) {
583

    
584
                                        if (!pc.isFollowingLine()) {
585
                                                lc.draw(g, labelMetrics, geom);
586
                                        } else {
587

    
588
                                                ILabelClass smsLc = new SmartTextSymbolLabelClass();
589
                                                SmartTextSymbol sms = new SmartTextSymbol(
590
                                                                lc.getTextSymbol(), pc);
591

    
592
                                                double sizeBefore = lc.getTextSymbol().getFont()
593
                                                                .getSize();
594
                                                double sizeAfter = SymbolUtils.getCartographicLength(
595
                                                                this, sizeBefore, viewPort, viewPort.getDPI());
596
                                                sms.setFontSize(sizeAfter);
597

    
598
                                                smsLc.setTextSymbol(sms);
599
                                                geom.transform(viewPort.getAffineTransform());
600
                                                smsLc.draw(g, null, geom);
601
                                                sms.setFontSize(sizeBefore);
602

    
603
                                        }
604
                                        return true;
605
                                }
606
                        } else {
607
                                if (!pc.isFollowingLine()) {
608
                                        lc.draw(g, labelMetrics, null);
609
                                } else {
610
                                        ILabelClass smsLc = new SmartTextSymbolLabelClass();
611
                                        SmartTextSymbol sms = new SmartTextSymbol(
612
                                                        lc.getTextSymbol(), pc);
613

    
614
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
615
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
616
                                                        sizeBefore, viewPort, viewPort.getDPI());
617
                                        sms.setFontSize(sizeAfter);
618

    
619
                                        smsLc.setTextSymbol(sms);
620
                                        geom.transform(viewPort.getAffineTransform());
621
                                        smsLc.draw(g, null, geom);
622

    
623
                                        sms.setFontSize(sizeBefore);
624
                                }
625
                                return true;
626
                        }
627
                }
628
                return false;
629
        }
630

    
631
        /**
632
         * Divide una cadena de caracteres por el caracter dos puntos siempre que no
633
         * est? entre comillas.
634
         *
635
         * @param str
636
         *            Cadena de caracteres
637
         *
638
         * @return String[]
639
         *
640
         */
641
        private String[] divideExpression(String str) {
642
                ArrayList<String> r = new ArrayList<String>();
643
                boolean inQuotationMarks = false;
644
                int lastIndex = 0;
645
                for (int i = 0; i < str.length(); i++) {
646
                        String currentChar = str.substring(i, i + 1);
647
                        if (currentChar.compareTo("\"") == 0 ) {
648
                                inQuotationMarks = !inQuotationMarks;
649
                                // Si es el cierre de las comillas
650
                                if(!inQuotationMarks){
651
                                        r.add(str.substring(lastIndex, i + 1).replace("\"", "'"));
652
                                        lastIndex = i + 1;
653
                                }
654
                        }
655
                        if (currentChar.compareTo(":") == 0
656
                                        && !inQuotationMarks) {
657
                                if (lastIndex < i) {
658
                                        r.add(str.substring(lastIndex, i));
659
                                }
660
                                lastIndex = i + 1;
661
                        }
662
                }
663
                if (lastIndex < str.length() - 1) {
664
                        r.add(str.substring(lastIndex));
665
                }
666
                String[] result = new String[r.size()];
667
                r.toArray(result);
668
                return result;
669
        }
670

    
671
        /**
672
         * Compute the texts to show in the label and store them in LabelClass.
673
         */
674
        @SuppressWarnings("unchecked")
675
        private boolean setupLabel(Feature featu, ILabelClass lc,
676
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
677
                        double dpi, int duplicateMode) {
678

    
679
                String expr = lc.getStringLabelExpression();
680

    
681
                long pt1 = System.currentTimeMillis();
682
                String[] texts = NO_TEXT;
683
                List<String> preTexts = new ArrayList<String>();
684
                try {
685
                        if (expr != null) {
686

    
687
                                if (expr.equals("")) {
688
                                        expr = texts[0];
689
                                }
690

    
691
                                String[] multiexpr = divideExpression(expr);
692
                                for (int i = 0; i < multiexpr.length; i++) {
693

    
694
                                        expr = multiexpr[i];
695
                                        Object res = LabelClassUtils.evaluate(expr,
696
                                                        featu.getEvaluatorData());
697
                                        if (res != null) {
698
                                                preTexts.add(res.toString());
699
                                        } else {
700
                                                preTexts.add("");
701
                                        }
702
                                }
703
                                texts = new String[preTexts.size()];
704
                                preTexts.toArray(texts);
705
                                // parseTime += System.currentTimeMillis()-pt1;
706
                        }
707
                        lc.setTexts(texts);
708

    
709
                } catch (Exception e) {
710
                        logger.warn("While setting up label", e);
711
                        return false;
712
                }
713
                return true;
714
        }
715

    
716
    private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
717

    
718
        for (Iterator iterator = drawnGeometryLabels.iterator(); iterator.hasNext();) {
719
            Geometry drawnGeometry = (Geometry) iterator.next();
720
            try {
721
                if (drawnGeometry.intersects(lblgeom)) {
722
                    return true;
723
                }
724
            } catch (GeometryOperationNotSupportedException | GeometryOperationException e) {
725
                logger.warn("Can't check overlapping geometry");
726
            }
727
        }
728
        drawnGeometryLabels.add(lblgeom);
729
        return false;
730

    
731
    }
732

    
733
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
734

    
735
                boolean onePoint = false;
736
                int shapeType = geom.getType();
737

    
738
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
739

    
740
                        Envelope env = geom.getEnvelope();
741
                        double dist1Pixel = viewPort.getDist1pixel();
742
                        onePoint = (env.getLength(0) <= dist1Pixel && env.getLength(1) <= dist1Pixel);
743
                }
744
                return onePoint;
745
        }
746

    
747
        public boolean isAllowingOverlap() {
748
                return allowOverlapping;
749
        }
750

    
751
        public void setAllowOverlapping(boolean allowOverlapping) {
752
                this.allowOverlapping = allowOverlapping;
753
        }
754

    
755
        public IPlacementConstraints getPlacementConstraints() {
756
                if (placementConstraints != null)
757
                        return placementConstraints;
758

    
759
                GeometryType gt = null;
760

    
761
                try {
762
                        gt = layer.getGeometryType();
763
                        // force 2d for comparison
764
                        gt = GeometryLocator.getGeometryManager().getGeometryType(
765
                                        gt.getType(), SUBTYPES.GEOM2D);
766
                } catch (Exception e) {
767
                        logger.error("While getting placements constraints.", e);
768
                        return null;
769
                }
770

    
771
                if (gt.isTypeOf(TYPES.POINT) || gt.isTypeOf(TYPES.MULTIPOINT)) {
772
                        return DefaultPointPlacementConstraints;
773
                } else {
774
                        if (gt.isTypeOf(TYPES.CURVE) || gt.isTypeOf(TYPES.MULTICURVE)) {
775
                                return DefaultLinePlacementConstraints;
776
                        } else {
777
                                if (gt.isTypeOf(TYPES.SURFACE)
778
                                                || gt.isTypeOf(TYPES.MULTISURFACE)) {
779
                                        return DefaultPolygonPlacementConstraints;
780
                                } else {
781
                                        if (gt.isTypeOf(TYPES.AGGREGATE)
782
                                                        || gt.isTypeOf(TYPES.GEOMETRY)) {
783
                                                DefaultMultiShapePlacementConstratints
784
                                                                .setPointConstraints(DefaultPointPlacementConstraints);
785
                                                DefaultMultiShapePlacementConstratints
786
                                                                .setLineConstraints(DefaultLinePlacementConstraints);
787
                                                DefaultMultiShapePlacementConstratints
788
                                                                .setPolygonConstraints(DefaultPolygonPlacementConstraints);
789
                                                return DefaultMultiShapePlacementConstratints;
790
                                        }
791
                                }
792
                        }
793
                }
794
                return null;
795
        }
796

    
797
        public void setPlacementConstraints(IPlacementConstraints constraints) {
798
                this.placementConstraints = constraints;
799
        }
800

    
801
        public IZoomConstraints getZoomConstraints() {
802
                return zoomConstraints;
803
        }
804

    
805
        public void setZoomConstraints(IZoomConstraints constraints) {
806
                this.zoomConstraints = constraints;
807
        }
808

    
809
        public void print(Graphics2D g, double scale, ViewPort viewPort,
810
                        Cancellable cancel, PrintAttributes properties)
811
                        throws ReadException {
812

    
813
                double dpi = 100;
814
                int pq = properties.getPrintQuality();
815
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL) {
816
                        dpi = 300;
817
                } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH) {
818
                        dpi = 600;
819
                } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT) {
820
                        dpi = 72;
821
                }
822

    
823
                viewPort.setOffset(new Point2D.Double(0, 0));
824

    
825
                /* signal printing output */
826
                printMode = true;
827

    
828
                draw(null, g, scale, viewPort, cancel, dpi);
829
        }
830

    
831
        public String[] getUsedFields() {
832

    
833
                /*
834
                 * TODO Solve the problem with the [ and ]. Currently SQLJEP evaluator
835
                 * cannot tell which fields are being used. Options: allow [] and remove
836
                 * them or maybe while parsing the SQLJEP evaluator can inform with
837
                 * events like "I have found a field"
838
                 */
839

    
840
                FeatureAttributeDescriptor[] atts = null;
841
                try {
842
                        atts = layer.getFeatureStore().getDefaultFeatureType()
843
                                        .getAttributeDescriptors();
844
                } catch (DataException e) {
845
                        logger.error("While getting atributes.", e);
846
                }
847

    
848
                int n = atts.length;
849
                String[] resp = new String[n];
850
                for (int i = 0; i < n; i++) {
851
                        resp[i] = atts[i].getName();
852
                }
853
                return resp;
854

    
855
        }
856

    
857
        public boolean shouldDrawLabels(double scale) {
858
                double minScaleView = -1;
859
                double maxScaleView = -1;
860

    
861
                if (zoomConstraints != null) {
862
                        minScaleView = zoomConstraints.getMinScale();
863
                        maxScaleView = zoomConstraints.getMaxScale();
864
                }
865

    
866
                if (minScaleView == -1 && maxScaleView == -1) {
867
                        // parameters not set, so the layer decides.
868
                        return layer.isWithinScale(scale);
869
                }
870

    
871
                if (minScaleView >= scale) {
872
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
873
                }
874

    
875
                return false;
876
        }
877

    
878
        public void setUnit(int unitIndex) {
879
                unit = unitIndex;
880

    
881
        }
882

    
883
        public int getUnit() {
884
                return unit;
885
        }
886

    
887
        public int getReferenceSystem() {
888
                return referenceSystem;
889
        }
890

    
891
        public void setReferenceSystem(int referenceSystem) {
892
                this.referenceSystem = referenceSystem;
893
        }
894

    
895
        public static void registerPersistent() {
896

    
897
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
898
                if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) {
899
                        DynStruct definition = manager.addDefinition(
900
                                        GeneralLabelingStrategy.class,
901
                                        GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME,
902
                                        GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME
903
                                                        + " Persistence definition", null, null);
904
                        definition.addDynFieldObject("labelingMethod")
905
                                        .setClassOfValue(ILabelingMethod.class).setMandatory(true);
906
                        definition.addDynFieldObject("placementConstraints")
907
                                        .setClassOfValue(IPlacementConstraints.class)
908
                                        .setMandatory(false);
909
                        definition.addDynFieldObject("zoomConstraints")
910
                                        .setClassOfValue(IZoomConstraints.class)
911
                                        .setMandatory(false);
912

    
913
                        definition.addDynFieldBoolean("allowOverlapping")
914
                                        .setMandatory(true);
915
                        definition.addDynFieldInt("unit").setMandatory(true);
916
                        definition.addDynFieldInt("referenceSystem").setMandatory(true);
917
                }
918
        }
919

    
920
        public void loadFromState(PersistentState state)
921
                        throws PersistenceException {
922

    
923
                method = (ILabelingMethod) state.get("labelingMethod");
924

    
925
                if (state.hasValue("placementConstraints")) {
926
                        placementConstraints = (IPlacementConstraints) state
927
                                        .get("placementConstraints");
928
                }
929

    
930
                if (state.hasValue("zoomConstraints")) {
931
                        zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
932
                }
933

    
934
                this.allowOverlapping = state.getBoolean("allowOverlapping");
935
                this.unit = state.getInt("unit");
936
                this.referenceSystem = state.getInt("referenceSystem");
937
        }
938

    
939
        public void saveToState(PersistentState state) throws PersistenceException {
940

    
941
                state.set("labelingMethod", method);
942

    
943
                if (placementConstraints != null) {
944
                        state.set("placementConstraints", placementConstraints);
945
                }
946

    
947
                if (zoomConstraints != null) {
948
                        state.set("zoomConstraints", zoomConstraints);
949
                }
950

    
951
                state.set("allowOverlapping", allowOverlapping);
952
                state.set("unit", unit);
953
                state.set("referenceSystem", referenceSystem);
954

    
955
        }
956

    
957
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
958
                /*
959
                 * This method is not used but we must implement CartographicSupport
960
                 */
961
                return 0;
962
        }
963

    
964
        public void setCartographicSize(double cartographicSize, Geometry geom) {
965
                /*
966
                 * This method is not used but we must implement CartographicSupport
967
                 */
968
        }
969

    
970
        public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) {
971
                /*
972
                 * This method is not used but we must implement CartographicSupport
973
                 */
974
                return 0;
975
        }
976

    
977
        public Object clone() throws CloneNotSupportedException {
978
                return LabelClassUtils.clone(this);
979
        }
980

    
981
}