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

History | View | Annotate | Download (30.7 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.HashMap;
115
import java.util.Iterator;
116
import java.util.List;
117
import java.util.Map;
118
import java.util.TreeMap;
119
import java.util.TreeSet;
120

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

    
172
import org.slf4j.Logger;
173
import org.slf4j.LoggerFactory;
174

    
175
/**
176
 *
177
 * GeneralLabelingStrategy.java
178
 *
179
 *
180
 * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 4, 2008
181
 *
182
 */
183
public class GeneralLabelingStrategy implements ILabelingStrategy, Cloneable,
184
                CartographicSupport {
185

    
186
        private static final Logger logger = LoggerFactory
187
                        .getLogger(GeneralLabelingStrategy.class);
188

    
189
        public static final String GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME = "GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME";
190

    
191
        public static PointPlacementConstraints DefaultPointPlacementConstraints = new PointPlacementConstraints();
192
        public static LinePlacementConstraints DefaultLinePlacementConstraints = new LinePlacementConstraints();
193
        public static PolygonPlacementConstraints DefaultPolygonPlacementConstraints = new PolygonPlacementConstraints();
194

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

    
197
        private static MultiShapePlacementConstraints DefaultMultiShapePlacementConstratints = new MultiShapePlacementConstraints();
198

    
199
        private ILabelingMethod method;
200
        private IPlacementConstraints placementConstraints;
201
        private IZoomConstraints zoomConstraints;
202

    
203
        private boolean allowOverlapping;
204

    
205
        protected FLyrVect layer;
206

    
207
        // private long parseTime;
208
        private int unit;
209
        private int referenceSystem;
210
        // private double sizeAfter;
211
        private boolean printMode = false; /*
212
                                                                                 * indicate whether output is for a
213
                                                                                 * print product (PDF, PS, ...)
214
                                                                                 */
215

    
216
        public GeneralLabelingStrategy() {
217
                method = SymbologyLocator.getSymbologyManager()
218
                                .createDefaultLabelingMethod();
219
        }
220

    
221
        public void setLayer(FLayer layer) {
222
                FLyrVect l = (FLyrVect) layer;
223
                this.layer = l;
224
        }
225

    
226
        public ILabelingMethod getLabelingMethod() {
227
                return method;
228
        }
229

    
230
        public void setLabelingMethod(ILabelingMethod method) {
231
                this.method = method;
232
        }
233

    
234
        private class GeometryItem {
235
                public Geometry geom = null;
236
                public int weigh = 0;
237
                public double savedPerimeter;
238

    
239
                public GeometryItem(Geometry geom, int weigh) {
240
                        this.geom = geom;
241
                        this.weigh = weigh;
242
                        this.savedPerimeter = 0;
243
                }
244
        }
245

    
246
        public void draw(BufferedImage mapImage, Graphics2D mapGraphics,
247
                        double scale, ViewPort viewPort, Cancellable cancel, double dpi)
248
                        throws ReadException {
249

    
250
                int x = (int) viewPort.getOffset().getX();
251
                int y = (int) viewPort.getOffset().getY();
252

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

    
263
                TreeMap<String[], GeometryItem> labelsToPlace = null;
264

    
265
                String[] usedFields = getUsedFields();
266

    
267
                int notPlacedCount = 0;
268
                int placedCount = 0;
269

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

    
276
                BufferedImage targetBI;
277
                Graphics2D targetGr;
278

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

    
286
                for (int i = 0; i < lcs.length; i++)
287
                        ts.add(lcs[i]);
288

    
289
                if (ts.size() == 0)
290
                        return;
291

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

    
300
                        if (!lc.isVisible(scale)) {
301
                                /*
302
                                 * Avoid non-visible labels
303
                                 */
304
                                continue;
305
                        }
306

    
307
                        FeatureSet fset = null;
308
                        DisposableIterator diter = null;
309
                        try {
310

    
311
                                try {
312
                                        fset = method.getFeatureIteratorByLabelClass(layer, lc,
313
                                                        viewPort, usedFields);
314
                                } catch (DataException e) {
315
                                        throw new ReadException(layer.getFeatureStore()
316
                                                        .getProviderName(), e);
317
                                }
318

    
319
                                // duplicates treatment stuff
320
                                /* handle the duplicates mode */
321
                                int duplicateMode = getDuplicateLabelsMode();
322
                                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
323
                                        // we need to register the labels already placed
324

    
325
                                        labelsToPlace = new TreeMap<String[], GeometryItem>(
326
                                                        new RemoveDuplicatesComparator());
327
                                }
328

    
329
                                boolean bLabelsReallocatable = !isAllowingOverlap();
330

    
331
                                BufferedImage overlapDetectImage = null;
332
                                Graphics2D overlapDetectGraphics = null;
333
                                if (bLabelsReallocatable) {
334
                                        int width = viewPort.getImageWidth() + print_offset_x;
335

    
336
                                        if (width < 0) {
337
                                                width = 1;
338
                                        }
339
                                        int height = viewPort.getImageHeight() + print_offset_y;
340
                                        if (height < 0) {
341
                                                height = 1;
342
                                        }
343
                                        if (mapImage != null)
344
                                                overlapDetectImage = new BufferedImage(
345
                                                                mapImage.getWidth() + print_offset_x,
346
                                                                mapImage.getHeight() + print_offset_y,
347
                                                                BufferedImage.TYPE_INT_ARGB);
348
                                        else
349
                                                overlapDetectImage = new BufferedImage(
350
                                                                viewPort.getImageWidth() + print_offset_x,
351
                                                                viewPort.getImageHeight() + print_offset_y,
352
                                                                BufferedImage.TYPE_INT_ARGB);
353

    
354
                                        overlapDetectGraphics = overlapDetectImage.createGraphics();
355
                                        overlapDetectGraphics.setRenderingHints(mapGraphics
356
                                                        .getRenderingHints());
357
                                }
358
                                if (bLabelsReallocatable) {
359
                                        targetBI = overlapDetectImage;
360
                                        targetGr = overlapDetectGraphics;
361
                                        targetGr.translate(-x, -y);
362
                                } else {
363
                                        targetBI = mapImage;
364
                                        targetGr = mapGraphics;
365
                                }
366

    
367
                                try {
368
                                        diter = fset.fastIterator();
369
                                } catch (DataException e) {
370
                                        throw new ReadException(layer.getFeatureStore()
371
                                                        .getProviderName(), e);
372
                                }
373
                                Feature featu = null;
374
                                Geometry geome = null;
375

    
376
                                while (!cancel.isCanceled() && diter.hasNext()) {
377

    
378
                                        featu = (Feature) diter.next();
379
                                        geome = featu.getDefaultGeometry();
380
                                        if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
381
                                                continue;
382
                                        }
383

    
384
                                        if (!setupLabel(featu, lc, cancel, usedFields, viewPort,
385
                                                        dpi, duplicateMode)) {
386
                                                continue;
387
                                        }
388

    
389
                                        String[] texts = lc.getTexts();
390

    
391
                                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
392
                                                // check if this text (so label) is already present in
393
                                                // the map
394

    
395
                                                GeometryItem item = labelsToPlace.get(texts);
396
                                                if (item == null) {
397
                                                        item = new GeometryItem(geome, 0);
398
                                                        labelsToPlace.put(texts, item);
399
                                                }
400
                                                if (item.geom != null) {
401

    
402
                                                        notPlacedCount++;
403
                                                        if (geome.getType() != Geometry.TYPES.POINT) {
404

    
405
                                                                Envelope auxBox = geome.getEnvelope();
406
                                                                double perimeterAux = 2 * (auxBox.getLength(0) + auxBox
407
                                                                                .getLength(1));
408
                                                                if (perimeterAux > item.savedPerimeter) {
409
                                                                        item.geom = geome; // FConverter.jts_to_igeometry(jtsGeom);
410
                                                                        item.savedPerimeter = perimeterAux;
411
                                                                }
412
                                                        } else {
413
                                                                int weigh = item.weigh;
414

    
415
                                                                try {
416
                                                                        Point pointFromLabel = item.geom.centroid();
417
                                                                        Point pointGeome = geome.centroid();
418
                                                                        item.geom = GeometryLocator
419
                                                                                        .getGeometryManager()
420
                                                                                        .createPoint(
421
                                                                                                        (pointFromLabel.getX()
422
                                                                                                                        * weigh + pointGeome.getX())
423
                                                                                                                        / (weigh + 1),
424
                                                                                                        (pointFromLabel.getY()
425
                                                                                                                        * weigh + pointGeome
426
                                                                                                                                .getY())
427
                                                                                                                        / (weigh + 1),
428
                                                                                                        Geometry.SUBTYPES.GEOM2D);
429
                                                                } catch (Exception ex) {
430
                                                                        throw new ReadException(layer
431
                                                                                        .getFeatureStore()
432
                                                                                        .getProviderName(), ex);
433
                                                                }
434

    
435
                                                        }
436
                                                } else {
437
                                                        item.geom = geome;
438
                                                }
439
                                                item.weigh++;
440
                                        } else {
441
                                                // Check if size is a pixel
442
                                                if (isOnePoint(viewPort, geome)) {
443
                                                        continue;
444
                                                }
445

    
446
                                                List<Geometry> geome_parts = new ArrayList<Geometry>();
447
                                                if (duplicateMode == IPlacementConstraints.ONE_LABEL_PER_FEATURE_PART) {
448
                                                        geome_parts = getGeometryParts(geome);
449
                                                } else {
450
                                                        geome_parts.add(geome);
451
                                                }
452

    
453
                                                try {
454
                                                        int n = geome_parts.size();
455
                                                        for (int k = 0; k < n; k++) {
456
                                                                drawLabelInGeom(targetBI, targetGr, lc,
457
                                                                                placement, viewPort,
458
                                                                                geome_parts.get(k), cancel, dpi,
459
                                                                                bLabelsReallocatable);
460
                                                        }
461
                                                } catch (GeometryException e) {
462
                                                        throw new ReadException(layer.getFeatureStore()
463
                                                                        .getProviderName(), e);
464
                                                }
465

    
466
                                                placedCount++;
467
                                        }
468
                                }
469

    
470
                                // ======= End iteration in feature set ====================
471

    
472
                                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
473
                                        Iterator<String[]> textsIt = labelsToPlace.keySet()
474
                                                        .iterator();
475
                                        while (!cancel.isCanceled() && textsIt.hasNext()) {
476
                                                notPlacedCount++;
477
                                                String[] texts = textsIt.next();
478

    
479
                                                GeometryItem item = labelsToPlace.get(texts);
480
                                                if (item != null) {
481
                                                        lc.setTexts(texts);
482
                                                        // Check if size is a pixel
483
                                                        if (isOnePoint(viewPort, item.geom)) {
484
                                                                continue;
485
                                                        }
486

    
487
                                                        try {
488
                                                                drawLabelInGeom(targetBI, targetGr, lc,
489
                                                                                placement, viewPort, item.geom, cancel,
490
                                                                                dpi, bLabelsReallocatable);
491
                                                        } catch (GeometryException e) {
492
                                                                throw new ReadException(layer.getFeatureStore()
493
                                                                                .getProviderName(), e);
494
                                                        }
495
                                                }
496
                                        }
497
                                }
498

    
499
                                if (bLabelsReallocatable) {
500
                                        targetGr.translate(x, y);
501
                                        if (mapImage != null) {
502
                                                mapGraphics.drawImage(overlapDetectImage, null, null);
503
                                        } else {
504
                                                mapGraphics.drawImage(overlapDetectImage, null, null);
505
                                        }
506
                                }
507

    
508
                        } finally {
509
                                if (diter != null) {
510
                                        diter.dispose();
511
                                }
512
                                if (fset != null){
513
                                        fset.dispose();
514
                                }
515
                        }
516
                } // big iteration
517

    
518
        }
519

    
520
        private List<Geometry> getGeometryParts(Geometry ge) {
521

    
522
                List<Geometry> resp = new ArrayList<Geometry>();
523
                if (ge != null) {
524
                        if (ge instanceof MultiPrimitive) {
525
                                MultiPrimitive mp = (MultiPrimitive) ge;
526
                                int n = mp.getPrimitivesNumber();
527
                                for (int i = 0; i < n; i++) {
528
                                        resp.add(mp.getPrimitiveAt(i));
529
                                }
530
                        } else {
531
                                resp.add(ge);
532
                        }
533
                }
534
                return resp;
535
        }
536

    
537
        private void drawLabelInGeom(BufferedImage targetBI, Graphics2D targetGr,
538
                        ILabelClass lc, ILabelPlacement placement, ViewPort viewPort,
539
                        Geometry geom, Cancellable cancel, double dpi,
540
                        boolean bLabelsReallocatable) throws GeometryException {
541

    
542
                lc.toCartographicSize(viewPort, dpi, null);
543
                ArrayList<LabelLocationMetrics> llm = null;
544
                llm = placement.guess(lc, geom, getPlacementConstraints(), 0, cancel,
545
                                viewPort);
546

    
547
                setReferenceSystem(lc.getReferenceSystem());
548
                setUnit(lc.getUnit());
549

    
550
                /*
551
                 * search if there is room left by the previous and with more priority
552
                 * labels, then check the current level
553
                 */
554
                lookupAndPlaceLabel(targetBI, targetGr, llm, placement, lc, geom,
555
                                viewPort, cancel, bLabelsReallocatable);
556

    
557
        }
558

    
559
        private int getDuplicateLabelsMode() {
560
                if (getPlacementConstraints() == null) {
561
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
562
                }
563
                return getPlacementConstraints().getDuplicateLabelsMode();
564
        }
565

    
566
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
567
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
568
                        ILabelClass lc, Geometry geom, ViewPort viewPort,
569
                        Cancellable cancel, boolean bLabelsReallocatable)
570
                        throws GeometryException {
571

    
572
                int i;
573
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
574
                        LabelLocationMetrics labelMetrics = llm.get(i);
575

    
576
                        IPlacementConstraints pc = getPlacementConstraints();
577
                        if (pc instanceof MultiShapePlacementConstraints) {
578
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints) pc;
579

    
580
                                GeometryManager gm = GeometryLocator.getGeometryManager();
581
                                GeometryType line_gt = null;
582
                                GeometryType polyg_gt = null;
583
                                GeometryType geom_gt = null;
584

    
585
                                line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
586
                                polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
587
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
588

    
589
                                if (geom_gt.getType() == TYPES.POINT
590
                                                || geom_gt.getType() == TYPES.MULTIPOINT) {
591
                                        pc = mpc.getPointConstraints();
592
                                } else {
593
                                        if (geom_gt.isTypeOf(TYPES.CURVE)
594
                                                        || geom_gt.getType() == TYPES.MULTICURVE) {
595
                                                pc = mpc.getLineConstraints();
596
                                        } else {
597
                                                if (geom_gt.isTypeOf(TYPES.SURFACE)
598
                                                                || geom_gt.getType() == TYPES.MULTISURFACE) {
599
                                                        pc = mpc.getPolygonConstraints();
600
                                                }
601
                                        }
602
                                }
603
                        }
604

    
605
                        /*
606
                         * Ver comentario en el metodo drawLabelInGeom
607
                         */
608
                        if (bLabelsReallocatable) {
609

    
610
                                Geometry aux_geom = null;
611
                                aux_geom = lc.getShape(labelMetrics);
612

    
613
                                if (!isOverlapping(bi, aux_geom)) {
614

    
615
                                        if (!pc.isFollowingLine()) {
616
                                                lc.draw(g, labelMetrics, geom);
617
                                        } else {
618

    
619
                                                ILabelClass smsLc = new SmartTextSymbolLabelClass();
620
                                                SmartTextSymbol sms = new SmartTextSymbol(
621
                                                                lc.getTextSymbol(), pc);
622

    
623
                                                double sizeBefore = lc.getTextSymbol().getFont()
624
                                                                .getSize();
625
                                                double sizeAfter = SymbolUtils.getCartographicLength(
626
                                                                this, sizeBefore, viewPort, viewPort.getDPI());
627
                                                sms.setFontSize(sizeAfter);
628

    
629
                                                smsLc.setTextSymbol(sms);
630
                                                geom.transform(viewPort.getAffineTransform());
631
                                                smsLc.draw(g, null, geom);
632
                                                sms.setFontSize(sizeBefore);
633

    
634
                                        }
635
                                        return true;
636
                                }
637
                        } else {
638
                                if (!pc.isFollowingLine()) {
639
                                        lc.draw(g, labelMetrics, null);
640
                                } else {
641
                                        ILabelClass smsLc = new SmartTextSymbolLabelClass();
642
                                        SmartTextSymbol sms = new SmartTextSymbol(
643
                                                        lc.getTextSymbol(), pc);
644

    
645
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
646
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
647
                                                        sizeBefore, viewPort, viewPort.getDPI());
648
                                        sms.setFontSize(sizeAfter);
649

    
650
                                        smsLc.setTextSymbol(sms);
651
                                        geom.transform(viewPort.getAffineTransform());
652
                                        smsLc.draw(g, null, geom);
653

    
654
                                        sms.setFontSize(sizeBefore);
655
                                }
656
                                return true;
657
                        }
658
                }
659
                return false;
660
        }
661

    
662
        /**
663
         * Divide una cadena de caracteres por el caracter dos puntos siempre que no
664
         * est? entre comillas.
665
         *
666
         * @param str
667
         *            Cadena de caracteres
668
         *
669
         * @return String[]
670
         *
671
         */
672
        private String[] divideExpression(String str) {
673
                ArrayList<String> r = new ArrayList<String>();
674
                boolean inQuotationMarks = false;
675
                int lastIndex = 0;
676
                for (int i = 0; i < str.length(); i++) {
677
                        String currentChar = str.substring(i, i + 1);
678
                        if (currentChar.compareTo("\"") == 0 ) {
679
                                inQuotationMarks = !inQuotationMarks;
680
                                // Si es el cierre de las comillas
681
                                if(!inQuotationMarks){
682
                                        r.add(str.substring(lastIndex, i + 1).replace("\"", "'"));
683
                                        lastIndex = i + 1;
684
                                }
685
                        }
686
                        if (currentChar.compareTo(":") == 0
687
                                        && !inQuotationMarks) {
688
                                if (lastIndex < i) {
689
                                        r.add(str.substring(lastIndex, i));
690
                                }
691
                                lastIndex = i + 1;
692
                        }
693
                }
694
                if (lastIndex < str.length() - 1) {
695
                        r.add(str.substring(lastIndex));
696
                }
697
                String[] result = new String[r.size()];
698
                r.toArray(result);
699
                return result;
700
        }
701

    
702
        /**
703
         * Compute the texts to show in the label and store them in LabelClass.
704
         */
705
        @SuppressWarnings("unchecked")
706
        private boolean setupLabel(Feature featu, ILabelClass lc,
707
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
708
                        double dpi, int duplicateMode) {
709

    
710
                String expr = lc.getStringLabelExpression();
711

    
712
                long pt1 = System.currentTimeMillis();
713
                String[] texts = NO_TEXT;
714
                List<String> preTexts = new ArrayList<String>();
715
                try {
716
                        if (expr != null) {
717

    
718
                                if (expr.equals("")) {
719
                                        expr = texts[0];
720
                                }
721

    
722
                                String[] multiexpr = divideExpression(expr);
723
                                for (int i = 0; i < multiexpr.length; i++) {
724

    
725
                                        expr = multiexpr[i];
726
                                        Object res = LabelClassUtils.evaluate(expr,
727
                                                        featu.getEvaluatorData());
728
                                        if (res != null) {
729
                                                preTexts.add(res.toString());
730
                                        } else {
731
                                                preTexts.add("");
732
                                        }
733
                                }
734
                                texts = new String[preTexts.size()];
735
                                preTexts.toArray(texts);
736
                                // parseTime += System.currentTimeMillis()-pt1;
737
                        }
738
                        lc.setTexts(texts);
739

    
740
                } catch (Exception e) {
741
                        logger.warn("While setting up label", e);
742
                        return false;
743
                }
744
                return true;
745
        }
746

    
747
        private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
748

    
749
                if (lblgeom == null || lblgeom.getType() == TYPES.NULL) {
750
                        return false;
751
                }
752

    
753
                Envelope rPixels = lblgeom.getEnvelope();
754
                int minx = (int) rPixels.getMinimum(0);
755
                int miny = (int) rPixels.getMinimum(1);
756
                int maxx = (int) rPixels.getMaximum(0);
757
                int maxy = (int) rPixels.getMaximum(1);
758

    
759
        for (int i = minx; i <= maxx; i++) {
760
            for (int j = miny; j <= maxy; j++) {
761

    
762
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
763
                try {
764
                    if (!lblgeom.contains(geomManager.createPoint(i, j, Geometry.SUBTYPES.GEOM2D))
765
                    // contains seems to don't detect points
766
                    // placed in the rectangle boundaries
767
                        && !lblgeom.intersects(geomManager.createEnvelope(i, j, i, j, Geometry.SUBTYPES.GEOM2D).getGeometry())) {
768
                        continue;
769
                    }
770
                } catch (CreateGeometryException | GeometryOperationNotSupportedException | GeometryOperationException
771
                    | CreateEnvelopeException e) {
772
                    logger.warn("Can't check overlapping with point ("+i+","+j+").");
773

    
774
                }
775

    
776
                if (i < 0 || j < 0) {
777
                    continue;
778
                }
779

    
780
                if (bi.getWidth() < i + 1 || bi.getHeight() < j + 1) {
781
                    continue;
782
                }
783

    
784
                if (bi.getRGB(i, j) != 0) {
785
                    return true;
786
                }
787
            }
788
        }
789
                return false;
790
        }
791

    
792
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
793

    
794
                boolean onePoint = false;
795
                int shapeType = geom.getType();
796

    
797
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
798

    
799
                        Envelope env = geom.getEnvelope();
800
                        double dist1Pixel = viewPort.getDist1pixel();
801
                        onePoint = (env.getLength(0) <= dist1Pixel && env.getLength(1) <= dist1Pixel);
802
                }
803
                return onePoint;
804
        }
805

    
806
        public boolean isAllowingOverlap() {
807
                return allowOverlapping;
808
        }
809

    
810
        public void setAllowOverlapping(boolean allowOverlapping) {
811
                this.allowOverlapping = allowOverlapping;
812
        }
813

    
814
        public IPlacementConstraints getPlacementConstraints() {
815
                if (placementConstraints != null)
816
                        return placementConstraints;
817

    
818
                GeometryType gt = null;
819

    
820
                try {
821
                        gt = layer.getGeometryType();
822
                        // force 2d for comparison
823
                        gt = GeometryLocator.getGeometryManager().getGeometryType(
824
                                        gt.getType(), SUBTYPES.GEOM2D);
825
                } catch (Exception e) {
826
                        logger.error("While getting placements constraints.", e);
827
                        return null;
828
                }
829

    
830
                if (gt.isTypeOf(TYPES.POINT) || gt.isTypeOf(TYPES.MULTIPOINT)) {
831
                        return DefaultPointPlacementConstraints;
832
                } else {
833
                        if (gt.isTypeOf(TYPES.CURVE) || gt.isTypeOf(TYPES.MULTICURVE)) {
834
                                return DefaultLinePlacementConstraints;
835
                        } else {
836
                                if (gt.isTypeOf(TYPES.SURFACE)
837
                                                || gt.isTypeOf(TYPES.MULTISURFACE)) {
838
                                        return DefaultPolygonPlacementConstraints;
839
                                } else {
840
                                        if (gt.isTypeOf(TYPES.AGGREGATE)
841
                                                        || gt.isTypeOf(TYPES.GEOMETRY)) {
842
                                                DefaultMultiShapePlacementConstratints
843
                                                                .setPointConstraints(DefaultPointPlacementConstraints);
844
                                                DefaultMultiShapePlacementConstratints
845
                                                                .setLineConstraints(DefaultLinePlacementConstraints);
846
                                                DefaultMultiShapePlacementConstratints
847
                                                                .setPolygonConstraints(DefaultPolygonPlacementConstraints);
848
                                                return DefaultMultiShapePlacementConstratints;
849
                                        }
850
                                }
851
                        }
852
                }
853
                return null;
854
        }
855

    
856
        public void setPlacementConstraints(IPlacementConstraints constraints) {
857
                this.placementConstraints = constraints;
858
        }
859

    
860
        public IZoomConstraints getZoomConstraints() {
861
                return zoomConstraints;
862
        }
863

    
864
        public void setZoomConstraints(IZoomConstraints constraints) {
865
                this.zoomConstraints = constraints;
866
        }
867

    
868
        public void print(Graphics2D g, double scale, ViewPort viewPort,
869
                        Cancellable cancel, PrintAttributes properties)
870
                        throws ReadException {
871

    
872
                double dpi = 100;
873
                int pq = properties.getPrintQuality();
874
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL) {
875
                        dpi = 300;
876
                } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH) {
877
                        dpi = 600;
878
                } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT) {
879
                        dpi = 72;
880
                }
881

    
882
                viewPort.setOffset(new Point2D.Double(0, 0));
883

    
884
                /* signal printing output */
885
                printMode = true;
886

    
887
                draw(null, g, scale, viewPort, cancel, dpi);
888
        }
889

    
890
        public String[] getUsedFields() {
891

    
892
                /*
893
                 * TODO Solve the problem with the [ and ]. Currently SQLJEP evaluator
894
                 * cannot tell which fields are being used. Options: allow [] and remove
895
                 * them or maybe while parsing the SQLJEP evaluator can inform with
896
                 * events like "I have found a field"
897
                 */
898

    
899
                FeatureAttributeDescriptor[] atts = null;
900
                try {
901
                        atts = layer.getFeatureStore().getDefaultFeatureType()
902
                                        .getAttributeDescriptors();
903
                } catch (DataException e) {
904
                        logger.error("While getting atributes.", e);
905
                }
906

    
907
                int n = atts.length;
908
                String[] resp = new String[n];
909
                for (int i = 0; i < n; i++) {
910
                        resp[i] = atts[i].getName();
911
                }
912
                return resp;
913

    
914
        }
915

    
916
        public boolean shouldDrawLabels(double scale) {
917
                double minScaleView = -1;
918
                double maxScaleView = -1;
919

    
920
                if (zoomConstraints != null) {
921
                        minScaleView = zoomConstraints.getMinScale();
922
                        maxScaleView = zoomConstraints.getMaxScale();
923
                }
924

    
925
                if (minScaleView == -1 && maxScaleView == -1) {
926
                        // parameters not set, so the layer decides.
927
                        return layer.isWithinScale(scale);
928
                }
929

    
930
                if (minScaleView >= scale) {
931
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
932
                }
933

    
934
                return false;
935
        }
936

    
937
        public void setUnit(int unitIndex) {
938
                unit = unitIndex;
939

    
940
        }
941

    
942
        public int getUnit() {
943
                return unit;
944
        }
945

    
946
        public int getReferenceSystem() {
947
                return referenceSystem;
948
        }
949

    
950
        public void setReferenceSystem(int referenceSystem) {
951
                this.referenceSystem = referenceSystem;
952
        }
953

    
954
        public static void registerPersistent() {
955

    
956
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
957
                if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) {
958
                        DynStruct definition = manager.addDefinition(
959
                                        GeneralLabelingStrategy.class,
960
                                        GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME,
961
                                        GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME
962
                                                        + " Persistence definition", null, null);
963
                        definition.addDynFieldObject("labelingMethod")
964
                                        .setClassOfValue(ILabelingMethod.class).setMandatory(true);
965
                        definition.addDynFieldObject("placementConstraints")
966
                                        .setClassOfValue(IPlacementConstraints.class)
967
                                        .setMandatory(false);
968
                        definition.addDynFieldObject("zoomConstraints")
969
                                        .setClassOfValue(IZoomConstraints.class)
970
                                        .setMandatory(false);
971

    
972
                        definition.addDynFieldBoolean("allowOverlapping")
973
                                        .setMandatory(true);
974
                        definition.addDynFieldInt("unit").setMandatory(true);
975
                        definition.addDynFieldInt("referenceSystem").setMandatory(true);
976
                }
977
        }
978

    
979
        public void loadFromState(PersistentState state)
980
                        throws PersistenceException {
981

    
982
                method = (ILabelingMethod) state.get("labelingMethod");
983

    
984
                if (state.hasValue("placementConstraints")) {
985
                        placementConstraints = (IPlacementConstraints) state
986
                                        .get("placementConstraints");
987
                }
988

    
989
                if (state.hasValue("zoomConstraints")) {
990
                        zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
991
                }
992

    
993
                this.allowOverlapping = state.getBoolean("allowOverlapping");
994
                this.unit = state.getInt("unit");
995
                this.referenceSystem = state.getInt("referenceSystem");
996
        }
997

    
998
        public void saveToState(PersistentState state) throws PersistenceException {
999

    
1000
                state.set("labelingMethod", method);
1001

    
1002
                if (placementConstraints != null) {
1003
                        state.set("placementConstraints", placementConstraints);
1004
                }
1005

    
1006
                if (zoomConstraints != null) {
1007
                        state.set("zoomConstraints", zoomConstraints);
1008
                }
1009

    
1010
                state.set("allowOverlapping", allowOverlapping);
1011
                state.set("unit", unit);
1012
                state.set("referenceSystem", referenceSystem);
1013

    
1014
        }
1015

    
1016
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1017
                /*
1018
                 * This method is not used but we must implement CartographicSupport
1019
                 */
1020
                return 0;
1021
        }
1022

    
1023
        public void setCartographicSize(double cartographicSize, Geometry geom) {
1024
                /*
1025
                 * This method is not used but we must implement CartographicSupport
1026
                 */
1027
        }
1028

    
1029
        public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1030
                /*
1031
                 * This method is not used but we must implement CartographicSupport
1032
                 */
1033
                return 0;
1034
        }
1035

    
1036
        public Object clone() throws CloneNotSupportedException {
1037
                return LabelClassUtils.clone(this);
1038
        }
1039

    
1040
}