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

History | View | Annotate | Download (29.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.primitive.Envelope;
135
import org.gvsig.fmap.geom.primitive.Point;
136
import org.gvsig.fmap.geom.type.GeometryType;
137
import org.gvsig.fmap.mapcontext.ViewPort;
138
import org.gvsig.fmap.mapcontext.layers.FLayer;
139
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
140
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelClass;
141
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingMethod;
142
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingStrategy;
143
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IPlacementConstraints;
144
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IZoomConstraints;
145
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
146
import org.gvsig.i18n.Messages;
147
import org.gvsig.labeling.lang.LabelClassUtils;
148
import org.gvsig.labeling.placements.ILabelPlacement;
149
import org.gvsig.labeling.placements.LinePlacementConstraints;
150
import org.gvsig.labeling.placements.MultiShapePlacementConstraints;
151
import org.gvsig.labeling.placements.PlacementManager;
152
import org.gvsig.labeling.placements.PointPlacementConstraints;
153
import org.gvsig.labeling.placements.PolygonPlacementConstraints;
154
import org.gvsig.labeling.placements.RemoveDuplicatesComparator;
155
import org.gvsig.labeling.symbol.SmartTextSymbol;
156
import org.gvsig.labeling.symbol.SymbolUtils;
157
import org.gvsig.symbology.SymbologyLocator;
158
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.styling.LabelLocationMetrics;
159
import org.gvsig.tools.ToolsLocator;
160
import org.gvsig.tools.dispose.DisposableIterator;
161
import org.gvsig.tools.dynobject.DynStruct;
162
import org.gvsig.tools.persistence.PersistenceManager;
163
import org.gvsig.tools.persistence.PersistentState;
164
import org.gvsig.tools.persistence.exception.PersistenceException;
165
import org.gvsig.tools.task.Cancellable;
166
import org.slf4j.Logger;
167
import org.slf4j.LoggerFactory;
168

    
169
/**
170
 *
171
 * GeneralLabelingStrategy.java
172
 *
173
 *
174
 * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 4, 2008
175
 *
176
 */
177
public class GeneralLabelingStrategy implements ILabelingStrategy, Cloneable,
178
                CartographicSupport {
179

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

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

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

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

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

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

    
197
        private boolean allowOverlapping;
198

    
199
        protected FLyrVect layer;
200

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

    
210
        public GeneralLabelingStrategy() {
211
                method = SymbologyLocator.getSymbologyManager()
212
                                .createDefaultLabelingMethod();
213
        }
214

    
215
        public void setLayer(FLayer layer) {
216
                FLyrVect l = (FLyrVect) layer;
217
                this.layer = l;
218
        }
219

    
220
        public ILabelingMethod getLabelingMethod() {
221
                return method;
222
        }
223

    
224
        public void setLabelingMethod(ILabelingMethod method) {
225
                this.method = method;
226
        }
227

    
228
        private class GeometryItem {
229
                public Geometry geom = null;
230
                public int weigh = 0;
231
                public double savedPerimeter;
232

    
233
                public GeometryItem(Geometry geom, int weigh) {
234
                        this.geom = geom;
235
                        this.weigh = weigh;
236
                        this.savedPerimeter = 0;
237
                }
238
        }
239

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

    
244
                int x = (int) viewPort.getOffset().getX();
245
                int y = (int) viewPort.getOffset().getY();
246

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

    
257
                TreeMap<String[], GeometryItem> labelsToPlace = null;
258

    
259
                String[] usedFields = getUsedFields();
260

    
261
                int notPlacedCount = 0;
262
                int placedCount = 0;
263

    
264
                /*
265
                 * Get the label placement solvers according the user's settings
266
                 */
267
                ILabelPlacement placement = PlacementManager.getPlacement(
268
                                getPlacementConstraints(), layer.getShapeType());
269

    
270
                BufferedImage targetBI;
271
                Graphics2D targetGr;
272

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

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

    
283
                if (ts.size() == 0)
284
                        return;
285

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

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

    
301
                        FeatureSet fset = null;
302
                        DisposableIterator diter = null;
303
                        try {
304

    
305
                                try {
306
                                        fset = method.getFeatureIteratorByLabelClass(layer, lc,
307
                                                        viewPort, usedFields);
308
                                } catch (DataException e) {
309
                                        throw new ReadException(layer.getFeatureStore()
310
                                                        .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>(
320
                                                        new RemoveDuplicatesComparator());
321
                                }
322

    
323
                                boolean bLabelsReallocatable = !isAllowingOverlap();
324

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

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

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

    
361
                                try {
362
                                        diter = fset.fastIterator();
363
                                } catch (DataException e) {
364
                                        throw new ReadException(layer.getFeatureStore()
365
                                                        .getProviderName(), e);
366
                                }
367
                                Feature featu = null;
368
                                Geometry geome = null;
369

    
370
                                while (!cancel.isCanceled() && diter.hasNext()) {
371

    
372
                                        featu = (Feature) diter.next();
373
                                        geome = featu.getDefaultGeometry();
374
                                        if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
375
                                                continue;
376
                                        }
377

    
378
                                        if (!setupLabel(featu, lc, cancel, usedFields, viewPort,
379
                                                        dpi, duplicateMode)) {
380
                                                continue;
381
                                        }
382

    
383
                                        String[] texts = lc.getTexts();
384

    
385
                                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
386
                                                // check if this text (so label) is already present in
387
                                                // the map
388

    
389
                                                GeometryItem item = labelsToPlace.get(texts);
390
                                                if (item == null) {
391
                                                        item = new GeometryItem(geome, 0);
392
                                                        labelsToPlace.put(texts, item);
393
                                                }
394
                                                if (item.geom != null) {
395

    
396
                                                        notPlacedCount++;
397
                                                        if (geome.getType() != Geometry.TYPES.POINT) {
398

    
399
                                                                Envelope auxBox = geome.getEnvelope();
400
                                                                double perimeterAux = 2 * (auxBox.getLength(0) + auxBox
401
                                                                                .getLength(1));
402
                                                                if (perimeterAux > item.savedPerimeter) {
403
                                                                        item.geom = geome; // FConverter.jts_to_igeometry(jtsGeom);
404
                                                                        item.savedPerimeter = perimeterAux;
405
                                                                }
406
                                                        } else {
407
                                                                int weigh = item.weigh;
408

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

    
429
                                                        }
430
                                                } else {
431
                                                        item.geom = geome;
432
                                                }
433
                                                item.weigh++;
434
                                        } else {
435
                                                // Check if size is a pixel
436
                                                if (isOnePoint(viewPort, geome)) {
437
                                                        continue;
438
                                                }
439

    
440
                                                List<Geometry> geome_parts = new ArrayList<Geometry>();
441
                                                if (duplicateMode == IPlacementConstraints.ONE_LABEL_PER_FEATURE_PART) {
442
                                                        geome_parts = getGeometryParts(geome);
443
                                                } else {
444
                                                        geome_parts.add(geome);
445
                                                }
446

    
447
                                                try {
448
                                                        int n = geome_parts.size();
449
                                                        for (int k = 0; k < n; k++) {
450
                                                                drawLabelInGeom(targetBI, targetGr, lc,
451
                                                                                placement, viewPort,
452
                                                                                geome_parts.get(k), cancel, dpi,
453
                                                                                bLabelsReallocatable);
454
                                                        }
455
                                                } catch (GeometryException e) {
456
                                                        throw new ReadException(layer.getFeatureStore()
457
                                                                        .getProviderName(), e);
458
                                                }
459

    
460
                                                placedCount++;
461
                                        }
462
                                }
463

    
464
                                // ======= End iteration in feature set ====================
465

    
466
                                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
467
                                        Iterator<String[]> textsIt = labelsToPlace.keySet()
468
                                                        .iterator();
469
                                        while (!cancel.isCanceled() && textsIt.hasNext()) {
470
                                                notPlacedCount++;
471
                                                String[] texts = textsIt.next();
472

    
473
                                                GeometryItem item = labelsToPlace.get(texts);
474
                                                if (item != null) {
475
                                                        lc.setTexts(texts);
476
                                                        // Check if size is a pixel
477
                                                        if (isOnePoint(viewPort, item.geom)) {
478
                                                                continue;
479
                                                        }
480

    
481
                                                        try {
482
                                                                drawLabelInGeom(targetBI, targetGr, lc,
483
                                                                                placement, viewPort, item.geom, cancel,
484
                                                                                dpi, bLabelsReallocatable);
485
                                                        } catch (GeometryException e) {
486
                                                                throw new ReadException(layer.getFeatureStore()
487
                                                                                .getProviderName(), e);
488
                                                        }
489
                                                }
490
                                        }
491
                                }
492

    
493
                                if (bLabelsReallocatable) {
494
                                        targetGr.translate(x, y);
495
                                        if (mapImage != null) {
496
                                                mapGraphics.drawImage(overlapDetectImage, null, null);
497
                                        } else {
498
                                                mapGraphics.drawImage(overlapDetectImage, null, null);
499
                                        }
500
                                }
501

    
502
                        } finally {
503
                                if (diter != null) {
504
                                        diter.dispose();
505
                                }
506
                                if (fset != null){
507
                                        fset.dispose();
508
                                }
509
                        }
510
                } // big iteration
511

    
512
        }
513

    
514
        private List<Geometry> getGeometryParts(Geometry ge) {
515

    
516
                List<Geometry> resp = new ArrayList<Geometry>();
517
                if (ge != null) {
518
                        if (ge instanceof MultiPrimitive) {
519
                                MultiPrimitive mp = (MultiPrimitive) ge;
520
                                int n = mp.getPrimitivesNumber();
521
                                for (int i = 0; i < n; i++) {
522
                                        resp.add(mp.getPrimitiveAt(i));
523
                                }
524
                        } else {
525
                                resp.add(ge);
526
                        }
527
                }
528
                return resp;
529
        }
530

    
531
        private void drawLabelInGeom(BufferedImage targetBI, Graphics2D targetGr,
532
                        ILabelClass lc, ILabelPlacement placement, ViewPort viewPort,
533
                        Geometry geom, Cancellable cancel, double dpi,
534
                        boolean bLabelsReallocatable) throws GeometryException {
535

    
536
                lc.toCartographicSize(viewPort, dpi, null);
537
                ArrayList<LabelLocationMetrics> llm = null;
538
                llm = placement.guess(lc, geom, getPlacementConstraints(), 0, cancel,
539
                                viewPort);
540

    
541
                setReferenceSystem(lc.getReferenceSystem());
542
                setUnit(lc.getUnit());
543

    
544
                /*
545
                 * search if there is room left by the previous and with more priority
546
                 * labels, then check the current level
547
                 */
548
                lookupAndPlaceLabel(targetBI, targetGr, llm, placement, lc, geom,
549
                                viewPort, cancel, bLabelsReallocatable);
550

    
551
        }
552

    
553
        private int getDuplicateLabelsMode() {
554
                if (getPlacementConstraints() == null) {
555
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
556
                }
557
                return getPlacementConstraints().getDuplicateLabelsMode();
558
        }
559

    
560
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
561
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
562
                        ILabelClass lc, Geometry geom, ViewPort viewPort,
563
                        Cancellable cancel, boolean bLabelsReallocatable)
564
                        throws GeometryException {
565

    
566
                int i;
567
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
568
                        LabelLocationMetrics labelMetrics = llm.get(i);
569

    
570
                        IPlacementConstraints pc = getPlacementConstraints();
571
                        if (pc instanceof MultiShapePlacementConstraints) {
572
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints) pc;
573

    
574
                                GeometryManager gm = GeometryLocator.getGeometryManager();
575
                                GeometryType line_gt = null;
576
                                GeometryType polyg_gt = null;
577
                                GeometryType geom_gt = null;
578

    
579
                                line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
580
                                polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
581
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
582

    
583
                                if (geom_gt.getType() == TYPES.POINT
584
                                                || geom_gt.getType() == TYPES.MULTIPOINT) {
585
                                        pc = mpc.getPointConstraints();
586
                                } else {
587
                                        if (geom_gt.isTypeOf(TYPES.CURVE)
588
                                                        || geom_gt.getType() == TYPES.MULTICURVE) {
589
                                                pc = mpc.getLineConstraints();
590
                                        } else {
591
                                                if (geom_gt.isTypeOf(TYPES.SURFACE)
592
                                                                || geom_gt.getType() == TYPES.MULTISURFACE) {
593
                                                        pc = mpc.getPolygonConstraints();
594
                                                }
595
                                        }
596
                                }
597
                        }
598

    
599
                        /*
600
                         * Ver comentario en el metodo drawLabelInGeom
601
                         */
602
                        if (bLabelsReallocatable) {
603

    
604
                                Geometry aux_geom = null;
605
                                aux_geom = lc.getShape(labelMetrics);
606

    
607
                                if (!isOverlapping(bi, aux_geom)) {
608

    
609
                                        if (!pc.isFollowingLine()) {
610
                                                lc.draw(g, labelMetrics, geom);
611
                                        } else {
612

    
613
                                                ILabelClass smsLc = new SmartTextSymbolLabelClass();
614
                                                SmartTextSymbol sms = new SmartTextSymbol(
615
                                                                lc.getTextSymbol(), pc);
616

    
617
                                                double sizeBefore = lc.getTextSymbol().getFont()
618
                                                                .getSize();
619
                                                double sizeAfter = SymbolUtils.getCartographicLength(
620
                                                                this, sizeBefore, viewPort, viewPort.getDPI());
621
                                                sms.setFontSize(sizeAfter);
622

    
623
                                                smsLc.setTextSymbol(sms);
624
                                                geom.transform(viewPort.getAffineTransform());
625
                                                smsLc.draw(g, null, geom);
626
                                                sms.setFontSize(sizeBefore);
627

    
628
                                        }
629
                                        return true;
630
                                }
631
                        } else {
632
                                if (!pc.isFollowingLine()) {
633
                                        lc.draw(g, labelMetrics, null);
634
                                } else {
635
                                        ILabelClass smsLc = new SmartTextSymbolLabelClass();
636
                                        SmartTextSymbol sms = new SmartTextSymbol(
637
                                                        lc.getTextSymbol(), pc);
638

    
639
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
640
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
641
                                                        sizeBefore, viewPort, viewPort.getDPI());
642
                                        sms.setFontSize(sizeAfter);
643

    
644
                                        smsLc.setTextSymbol(sms);
645
                                        geom.transform(viewPort.getAffineTransform());
646
                                        smsLc.draw(g, null, geom);
647

    
648
                                        sms.setFontSize(sizeBefore);
649
                                }
650
                                return true;
651
                        }
652
                }
653
                return false;
654
        }
655

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

    
696
        /**
697
         * Compute the texts to show in the label and store them in LabelClass.
698
         */
699
        @SuppressWarnings("unchecked")
700
        private boolean setupLabel(Feature featu, ILabelClass lc,
701
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
702
                        double dpi, int duplicateMode) {
703

    
704
                String expr = lc.getStringLabelExpression();
705

    
706
                long pt1 = System.currentTimeMillis();
707
                String[] texts = NO_TEXT;
708
                List<String> preTexts = new ArrayList<String>();
709
                try {
710
                        if (expr != null) {
711

    
712
                                if (expr.equals("")) {
713
                                        expr = texts[0];
714
                                }
715

    
716
                                String[] multiexpr = divideExpression(expr);
717
                                for (int i = 0; i < multiexpr.length; i++) {
718

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

    
734
                } catch (Exception e) {
735
                        logger.warn("While setting up label", e);
736
                        return false;
737
                }
738
                return true;
739
        }
740

    
741
        private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
742

    
743
                if (lblgeom == null || lblgeom.getType() == TYPES.NULL) {
744
                        return false;
745
                }
746

    
747
                Envelope rPixels = lblgeom.getEnvelope();
748
                int minx = (int) rPixels.getMinimum(0);
749
                int miny = (int) rPixels.getMinimum(1);
750
                int maxx = (int) rPixels.getMaximum(0);
751
                int maxy = (int) rPixels.getMaximum(1);
752

    
753
                for (int i = minx; i <= maxx; i++) {
754
                        for (int j = miny; j <= maxy; j++) {
755

    
756
                                if (!lblgeom.contains(i, j)
757
                                // contains seems to don't detect points
758
                                // placed in the rectangle boundaries
759
                                                && !lblgeom.intersects(i, j, i, j)) {
760
                                        continue;
761
                                }
762

    
763
                                if (i < 0 || j < 0) {
764
                                        continue;
765
                                }
766

    
767
                                if (bi.getWidth() < i + 1 || bi.getHeight() < j + 1) {
768
                                        continue;
769
                                }
770

    
771
                                if (bi.getRGB(i, j) != 0) {
772
                                        return true;
773
                                }
774
                        }
775
                }
776
                return false;
777
        }
778

    
779
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
780

    
781
                boolean onePoint = false;
782
                int shapeType = geom.getType();
783

    
784
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
785

    
786
                        Envelope env = geom.getEnvelope();
787
                        double dist1Pixel = viewPort.getDist1pixel();
788
                        onePoint = (env.getLength(0) <= dist1Pixel && env.getLength(1) <= dist1Pixel);
789
                }
790
                return onePoint;
791
        }
792

    
793
        public boolean isAllowingOverlap() {
794
                return allowOverlapping;
795
        }
796

    
797
        public void setAllowOverlapping(boolean allowOverlapping) {
798
                this.allowOverlapping = allowOverlapping;
799
        }
800

    
801
        public IPlacementConstraints getPlacementConstraints() {
802
                if (placementConstraints != null)
803
                        return placementConstraints;
804

    
805
                GeometryType gt = null;
806

    
807
                try {
808
                        gt = layer.getGeometryType();
809
                        // force 2d for comparison
810
                        gt = GeometryLocator.getGeometryManager().getGeometryType(
811
                                        gt.getType(), SUBTYPES.GEOM2D);
812
                } catch (Exception e) {
813
                        logger.error("While getting placements constraints.", e);
814
                        return null;
815
                }
816

    
817
                if (gt.isTypeOf(TYPES.POINT) || gt.isTypeOf(TYPES.MULTIPOINT)) {
818
                        return DefaultPointPlacementConstraints;
819
                } else {
820
                        if (gt.isTypeOf(TYPES.CURVE) || gt.isTypeOf(TYPES.MULTICURVE)) {
821
                                return DefaultLinePlacementConstraints;
822
                        } else {
823
                                if (gt.isTypeOf(TYPES.SURFACE)
824
                                                || gt.isTypeOf(TYPES.MULTISURFACE)) {
825
                                        return DefaultPolygonPlacementConstraints;
826
                                } else {
827
                                        if (gt.isTypeOf(TYPES.AGGREGATE)
828
                                                        || gt.isTypeOf(TYPES.GEOMETRY)) {
829
                                                DefaultMultiShapePlacementConstratints
830
                                                                .setPointConstraints(DefaultPointPlacementConstraints);
831
                                                DefaultMultiShapePlacementConstratints
832
                                                                .setLineConstraints(DefaultLinePlacementConstraints);
833
                                                DefaultMultiShapePlacementConstratints
834
                                                                .setPolygonConstraints(DefaultPolygonPlacementConstraints);
835
                                                return DefaultMultiShapePlacementConstratints;
836
                                        }
837
                                }
838
                        }
839
                }
840
                return null;
841
        }
842

    
843
        public void setPlacementConstraints(IPlacementConstraints constraints) {
844
                this.placementConstraints = constraints;
845
        }
846

    
847
        public IZoomConstraints getZoomConstraints() {
848
                return zoomConstraints;
849
        }
850

    
851
        public void setZoomConstraints(IZoomConstraints constraints) {
852
                this.zoomConstraints = constraints;
853
        }
854

    
855
        public void print(Graphics2D g, double scale, ViewPort viewPort,
856
                        Cancellable cancel, PrintAttributes properties)
857
                        throws ReadException {
858

    
859
                double dpi = 100;
860
                int pq = properties.getPrintQuality();
861
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL) {
862
                        dpi = 300;
863
                } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH) {
864
                        dpi = 600;
865
                } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT) {
866
                        dpi = 72;
867
                }
868

    
869
                viewPort.setOffset(new Point2D.Double(0, 0));
870

    
871
                /* signal printing output */
872
                printMode = true;
873

    
874
                draw(null, g, scale, viewPort, cancel, dpi);
875
        }
876

    
877
        public String[] getUsedFields() {
878

    
879
                /*
880
                 * TODO Solve the problem with the [ and ]. Currently SQLJEP evaluator
881
                 * cannot tell which fields are being used. Options: allow [] and remove
882
                 * them or maybe while parsing the SQLJEP evaluator can inform with
883
                 * events like "I have found a field"
884
                 */
885

    
886
                FeatureAttributeDescriptor[] atts = null;
887
                try {
888
                        atts = layer.getFeatureStore().getDefaultFeatureType()
889
                                        .getAttributeDescriptors();
890
                } catch (DataException e) {
891
                        logger.error("While getting atributes.", e);
892
                }
893

    
894
                int n = atts.length;
895
                String[] resp = new String[n];
896
                for (int i = 0; i < n; i++) {
897
                        resp[i] = atts[i].getName();
898
                }
899
                return resp;
900

    
901
        }
902

    
903
        public boolean shouldDrawLabels(double scale) {
904
                double minScaleView = -1;
905
                double maxScaleView = -1;
906

    
907
                if (zoomConstraints != null) {
908
                        minScaleView = zoomConstraints.getMinScale();
909
                        maxScaleView = zoomConstraints.getMaxScale();
910
                }
911

    
912
                if (minScaleView == -1 && maxScaleView == -1) {
913
                        // parameters not set, so the layer decides.
914
                        return layer.isWithinScale(scale);
915
                }
916

    
917
                if (minScaleView >= scale) {
918
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
919
                }
920

    
921
                return false;
922
        }
923

    
924
        public void setUnit(int unitIndex) {
925
                unit = unitIndex;
926

    
927
        }
928

    
929
        public int getUnit() {
930
                return unit;
931
        }
932

    
933
        public int getReferenceSystem() {
934
                return referenceSystem;
935
        }
936

    
937
        public void setReferenceSystem(int referenceSystem) {
938
                this.referenceSystem = referenceSystem;
939
        }
940

    
941
        public static void registerPersistent() {
942

    
943
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
944
                if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) {
945
                        DynStruct definition = manager.addDefinition(
946
                                        GeneralLabelingStrategy.class,
947
                                        GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME,
948
                                        GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME
949
                                                        + " Persistence definition", null, null);
950
                        definition.addDynFieldObject("labelingMethod")
951
                                        .setClassOfValue(ILabelingMethod.class).setMandatory(true);
952
                        definition.addDynFieldObject("placementConstraints")
953
                                        .setClassOfValue(IPlacementConstraints.class)
954
                                        .setMandatory(false);
955
                        definition.addDynFieldObject("zoomConstraints")
956
                                        .setClassOfValue(IZoomConstraints.class)
957
                                        .setMandatory(false);
958

    
959
                        definition.addDynFieldBoolean("allowOverlapping")
960
                                        .setMandatory(true);
961
                        definition.addDynFieldInt("unit").setMandatory(true);
962
                        definition.addDynFieldInt("referenceSystem").setMandatory(true);
963
                }
964
        }
965

    
966
        public void loadFromState(PersistentState state)
967
                        throws PersistenceException {
968

    
969
                method = (ILabelingMethod) state.get("labelingMethod");
970

    
971
                if (state.hasValue("placementConstraints")) {
972
                        placementConstraints = (IPlacementConstraints) state
973
                                        .get("placementConstraints");
974
                }
975

    
976
                if (state.hasValue("zoomConstraints")) {
977
                        zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
978
                }
979

    
980
                this.allowOverlapping = state.getBoolean("allowOverlapping");
981
                this.unit = state.getInt("unit");
982
                this.referenceSystem = state.getInt("referenceSystem");
983
        }
984

    
985
        public void saveToState(PersistentState state) throws PersistenceException {
986

    
987
                state.set("labelingMethod", method);
988

    
989
                if (placementConstraints != null) {
990
                        state.set("placementConstraints", placementConstraints);
991
                }
992

    
993
                if (zoomConstraints != null) {
994
                        state.set("zoomConstraints", zoomConstraints);
995
                }
996

    
997
                state.set("allowOverlapping", allowOverlapping);
998
                state.set("unit", unit);
999
                state.set("referenceSystem", referenceSystem);
1000

    
1001
        }
1002

    
1003
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1004
                /*
1005
                 * This method is not used but we must implement CartographicSupport
1006
                 */
1007
                return 0;
1008
        }
1009

    
1010
        public void setCartographicSize(double cartographicSize, Geometry geom) {
1011
                /*
1012
                 * This method is not used but we must implement CartographicSupport
1013
                 */
1014
        }
1015

    
1016
        public double getCartographicSize(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 Object clone() throws CloneNotSupportedException {
1024
                return LabelClassUtils.clone(this);
1025
        }
1026

    
1027
}