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

History | View | Annotate | Download (32.2 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2005 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41

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

    
110
import java.awt.Graphics2D;
111
import java.awt.geom.Point2D;
112
import java.awt.image.BufferedImage;
113
import java.util.ArrayList;
114
import java.util.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
178
ILabelingStrategy, Cloneable, CartographicSupport {
179
        
180
        private static final Logger logger = LoggerFactory.getLogger(
181
                        GeneralLabelingStrategy.class);
182
        
183
        public static final String GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME =
184
                        "GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME";
185
        
186
        public static PointPlacementConstraints DefaultPointPlacementConstraints =
187
                        new PointPlacementConstraints();
188
        public static LinePlacementConstraints DefaultLinePlacementConstraints =
189
                        new LinePlacementConstraints();
190
        public static PolygonPlacementConstraints DefaultPolygonPlacementConstraints =
191
                        new PolygonPlacementConstraints();
192
        
193
        private static String[] NO_TEXT = { Messages.getText("text_field") };
194
        
195
        private static MultiShapePlacementConstraints DefaultMultiShapePlacementConstratints =
196
                        new MultiShapePlacementConstraints();
197
        
198
        private ILabelingMethod method;
199
        private IPlacementConstraints placementConstraints;
200
        private IZoomConstraints zoomConstraints;
201
        
202
        private boolean allowOverlapping;
203
        
204
        protected FLyrVect layer;
205

    
206
        
207
//        private long parseTime;
208
        private int unit;
209
        private int referenceSystem;
210
        // private double sizeAfter;
211
        private boolean printMode = false; /* indicate whether output is for a print product (PDF, PS, ...) */
212
        
213
        public GeneralLabelingStrategy() {
214
                method =
215
                                SymbologyLocator.getSymbologyManager().createDefaultLabelingMethod();
216
        }
217

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

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

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

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

    
236
                public GeometryItem(Geometry geom, int weigh){
237
                        this.geom = geom;
238
                        this.weigh = weigh;
239
                        this.savedPerimeter = 0;
240
                }
241
        }
242
        public void draw(BufferedImage mapImage, Graphics2D mapGraphics,
243
                        double scale, ViewPort viewPort,
244
                        Cancellable cancel,
245
                        double dpi) throws ReadException {
246
                
247
                int x = (int)viewPort.getOffset().getX();
248
                int y = (int)viewPort.getOffset().getY();
249
//                boolean bVisualFXEnabled = false; // if true, the user can see how the labeling is drawing up
250

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

    
261
                TreeMap<String[], GeometryItem> labelsToPlace = null;
262
//                parseTime =0;
263
//                long t1 = System.currentTimeMillis();
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

    
277
                BufferedImage targetBI;
278
                Graphics2D targetGr;
279

    
280

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

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

    
291
                if (ts.size()==0) return;
292

    
293
                /*
294
                 * now we have an ordered set, it is only need to give a pass
295
                 * for each label class to render by priorities.
296
                 *
297
                 * If no priorities were defined, the following loop only executes
298
                 * once
299
                 */
300
                for (ILabelClass lc : ts) {
301
                        
302
                    if (!lc.isVisible(scale)) {
303
                        /*
304
                         * Avoid non-visible labels
305
                         */
306
                        continue;
307
                    }
308
                    
309
                        FeatureSet fset = null;
310
                        
311
                        try {
312
                                fset = method.getFeatureIteratorByLabelClass(
313
                                                layer, lc, viewPort, usedFields);
314
                        } catch (DataException e) {
315
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
316
                        }
317

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

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

    
328
                        boolean bLabelsReallocatable = !isAllowingOverlap();
329

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

    
335
                                if(width<0){
336
                                        width = 1;
337
                                }
338
                                int height = viewPort.getImageHeight() + print_offset_y;
339
                                if(height<0){
340
                                        height = 1;
341
                                }
342
                                if (mapImage!=null)
343
                                        overlapDetectImage = new BufferedImage(
344
                                                        mapImage.getWidth()  + print_offset_x,
345
                                                        mapImage.getHeight()  + print_offset_y,
346
                                                        BufferedImage.TYPE_INT_ARGB
347
                                );
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

    
355
                                overlapDetectGraphics = overlapDetectImage.createGraphics();
356
                                overlapDetectGraphics.setRenderingHints(mapGraphics.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
                        DisposableIterator diter = null;
368
                        try {
369
                                diter = fset.fastIterator();
370
                        } catch (DataException e) {
371
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
372
                        }
373
                        Feature featu = null;
374
                        Geometry geome = null;
375
                        while ( !cancel.isCanceled() && diter.hasNext()) {
376
                                
377
                                featu = (Feature) diter.next();
378
                                geome = featu.getDefaultGeometry();
379
                                if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
380
                                        continue;
381
                                }
382
                                        
383

    
384
                                if (!setupLabel(featu, lc, cancel,
385
                                                usedFields, viewPort, 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 the map
393

    
394
                                        GeometryItem item = labelsToPlace.get(texts);
395
                                        if (item == null){
396
                                                item = new GeometryItem(geome, 0);
397
                                                labelsToPlace.put(texts, item);
398
                                        }
399
                                        if (item.geom != null) {
400
                                                
401
                                                notPlacedCount++;
402
                                                if (geome.getType() != Geometry.TYPES.POINT) {
403
                                                        // FJP: Cambiamos la uni?n por una comprobaci?n de longitud, por ejemplo.
404
                                                        // La geometr?a con mayor longitud del bounding box ser? la que etiquetamos.
405
                                                        // Ser? inexacto, pero m?s r?pido. Solo lo queremos para saber qu? entidad etiquetamos
406
                                                        // El problema con la uni?n es que para l?neas va muy mal (no sabes lo que te va
407
                                                        // a etiquetar, y para pol?gonos ser? muy lenta. De todas formas, habr?a que evitar
408
                                                        // la conversi?n al JTS.
409
//                                                        Geometry jtsGeom = item.geom.toJTSGeometry().union(geom.toJTSGeometry());
410
//                                                        if (jtsGeom instanceof LineString) {
411
//                                                                CoordinateSequence cs = ((LineString) (jtsGeom)).getCoordinateSequence();
412
//                                                                CoordinateSequences.reverse(cs);
413
//                                                                jtsGeom = new LineString(cs, null);
414
//                                                        }
415
//                                                        item.geom = FConverter.jts_to_igeometry(jtsGeom);
416

    
417
                                                        Envelope auxBox = geome.getEnvelope();
418
                                                        double perimeterAux = 2 * (auxBox.getLength(0)+auxBox.getLength(1)); 
419
                                                        if (perimeterAux > item.savedPerimeter) {
420
                                                                item.geom = geome; //FConverter.jts_to_igeometry(jtsGeom);
421
                                                                item.savedPerimeter = perimeterAux;
422
                                                        }
423
                                                } else {
424
                                                        int weigh = item.weigh;
425
                                                        
426
                                                        try {
427
                                                                Point pointFromLabel = item.geom.centroid();
428
                                                                Point pointGeome = geome.centroid();
429
                                                                item.geom = GeometryLocator.getGeometryManager().createPoint(
430
                                                                                (pointFromLabel.getX()*weigh + pointGeome.getX())/(weigh+1),
431
                                                                                (pointFromLabel.getY()*weigh + pointGeome.getY())/(weigh+1),
432
                                                                                Geometry.SUBTYPES.GEOM2D);
433
                                                        } catch (Exception ex) {
434
                                                                throw new ReadException(layer.getFeatureStore().getProviderName(), ex);
435
                                                        }
436
                                                        
437
                                                }
438
                                        } else {
439
                                                item.geom = geome;
440
                                        }
441
                                        item.weigh++;
442
                                } else {
443
                                        // Check if size is a pixel
444
                                        if (isOnePoint(viewPort, geome)) {
445
                                                continue;
446
                                        }
447
                                        
448
                                        List<Geometry> geome_parts = new ArrayList<Geometry>();
449
                                        if (duplicateMode == IPlacementConstraints.ONE_LABEL_PER_FEATURE_PART) {
450
                                            geome_parts = getGeometryParts(geome);
451
                                        } else {
452
                                            geome_parts.add(geome);
453
                                        }
454

    
455
                                        try {
456
                                            int n = geome_parts.size();
457
                                            for (int k=0; k<n; k++) {
458
                                drawLabelInGeom(
459
                                targetBI, targetGr,
460
                                lc, placement,
461
                                viewPort, geome_parts.get(k),
462
                                cancel, dpi,
463
                                bLabelsReallocatable);
464
                                            }
465
                                        } catch (GeometryException e) {
466
                                                throw new ReadException(
467
                                                                layer.getFeatureStore().getProviderName(), e);
468
                                        }
469
                                        
470
                                        
471
                                        placedCount++;
472
                                }
473
                        }
474
                        
475
                        // ======= End iteration in feature set ====================
476
                        
477
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
478
                                Iterator<String[]> textsIt = labelsToPlace.keySet().iterator();
479
                                while ( !cancel.isCanceled() && textsIt.hasNext()) {
480
                                        notPlacedCount++;
481
                                        String[] texts = textsIt.next();
482

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

    
491
//                                                lc.toCartographicSize(viewPort, dpi, null);
492
                                                try {
493
                                                        drawLabelInGeom(targetBI, targetGr,
494
                                                                        lc, placement, viewPort, item.geom,
495
                                                                        cancel, dpi, bLabelsReallocatable);
496
                                                } catch (GeometryException e) {
497
                                                        throw new ReadException(
498
                                                                        layer.getFeatureStore().getProviderName(), e);
499
                                                }
500
                                        }
501
                                }
502
                        }
503

    
504
                        if (bLabelsReallocatable) {
505
                                targetGr.translate(x, y);
506
                                if (mapImage!=null) {
507
                                        // Graphics2D aux = (Graphics2D) mapImage.getGraphics();
508
                                        mapGraphics.drawImage(overlapDetectImage, null, null);
509
                                } else {
510
                                        mapGraphics.drawImage(overlapDetectImage, null, null);
511
                                }
512
                        }
513

    
514

    
515
                } // big iteration
516
//                double totalTime = (System.currentTimeMillis()-t1);
517
//
518
//                int total = placedCount+notPlacedCount;
519
//
520
//                if (total>0)
521
//                Logger.getLogger(getClass()).info("Labeled layer '"+layer.getName()+
522
//                "' "+totalTime/1000D+" seconds. "+placedCount+"/"+total+
523
//                " labels placed ("+NumberFormat.getInstance().
524
//                format(100*placedCount/(double) total)+"%)");
525
//
526
//                if (cancel.isCanceled()) {
527
//                        Logger.getLogger(getClass()).info("Layer labeling canceled: '"+
528
//                                        layer.getName()+"'");
529
//                } else {
530
//                        Logger.getLogger(getClass()).info("Total labels parse time = "+
531
//                                        parseTime+" ("+NumberFormat.getInstance().
532
//                                        format(parseTime*100/totalTime)+"%)");
533
//                }
534

    
535
        }
536
        
537
        
538
        private List<Geometry> getGeometryParts(Geometry ge) {
539
            
540
            List<Geometry> resp = new ArrayList<Geometry>();
541
            if (ge != null) {
542
                if (ge instanceof MultiPrimitive) {
543
                    MultiPrimitive mp = (MultiPrimitive) ge;
544
                    int n = mp.getPrimitivesNumber();
545
                    for (int i=0; i<n; i++) {
546
                        resp.add(mp.getPrimitiveAt(i));
547
                    }
548
                } else {
549
                    resp.add(ge);
550
                }
551
            }
552
        return resp;
553
    }
554

    
555
    private void drawLabelInGeom(
556
                        BufferedImage targetBI, Graphics2D targetGr, ILabelClass lc,
557
                        ILabelPlacement placement, ViewPort viewPort, Geometry geom,
558
                        Cancellable cancel,
559
                        double dpi, boolean bLabelsReallocatable) throws GeometryException {
560

    
561
                lc.toCartographicSize(viewPort, dpi, null);
562
                ArrayList<LabelLocationMetrics> llm = null;
563
                llm = placement.guess(
564
                                lc,
565
                                geom,
566
                                getPlacementConstraints(),
567
                                0,
568
                                cancel,viewPort);
569

    
570
                setReferenceSystem(lc.getReferenceSystem());
571
                setUnit(lc.getUnit());
572

    
573
                /*
574
                 * search if there is room left by the previous and
575
                 * with more priority labels, then check the current
576
                 * level
577
                 */
578
//                if (
579
                lookupAndPlaceLabel(targetBI, targetGr, llm,
580
                                placement, lc, geom, viewPort, cancel,        bLabelsReallocatable);  //{
581

    
582
//                        lc.getTextSymbol().setFontSize(sizeBefore);
583
//                }
584
//                lc.toCartographicSize(viewPort, dpi, null);
585

    
586
        }
587

    
588
        private int getDuplicateLabelsMode() {
589
                if (getPlacementConstraints() == null) {
590
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
591
                }
592
                return getPlacementConstraints().getDuplicateLabelsMode();
593
        }
594

    
595
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
596
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
597
                        ILabelClass lc, Geometry geom,        ViewPort viewPort,
598
                        Cancellable cancel, boolean bLabelsReallocatable)
599
        throws GeometryException {
600
                
601
                int i;
602
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
603
                        LabelLocationMetrics labelMetrics = llm.get(i);
604

    
605
                        IPlacementConstraints pc = getPlacementConstraints();
606
                        if(pc instanceof MultiShapePlacementConstraints) {
607
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints)pc;
608
                                
609
                                GeometryManager gm = GeometryLocator.getGeometryManager();
610
                                GeometryType line_gt = null;
611
                                GeometryType polyg_gt = null;
612
                                GeometryType geom_gt = null;
613
                                
614
                                line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
615
                                polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
616
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
617

    
618
                                if (geom_gt.getType() == TYPES.POINT ||
619
                                                geom_gt.getType() == TYPES.MULTIPOINT) {
620
                                        pc = mpc.getPointConstraints();
621
                                } else {
622
                                        if (geom_gt.isTypeOf(TYPES.CURVE) ||
623
                                                        geom_gt.getType() == TYPES.MULTICURVE) {
624
                                                pc = mpc.getLineConstraints();
625
                                        } else {
626
                                                if (geom_gt.isTypeOf(TYPES.SURFACE) ||
627
                                                                geom_gt.getType() == TYPES.MULTISURFACE) {
628
                                                        pc = mpc.getPolygonConstraints();
629
                                                }
630
                                        }
631
                                }
632
                        }
633

    
634
                        /*
635
                         * Ver comentario en el metodo drawLabelInGeom
636
                         */
637
//                        lc.getTextSymbol().setFontSize(sizeAfter);// * FConstant.FONT_HEIGHT_SCALE_FACTOR);
638
                        if (bLabelsReallocatable) {
639
                                
640
                                Geometry aux_geom = null;
641
                                aux_geom = lc.getShape(labelMetrics);
642
                                
643
                                if (!isOverlapping(bi, aux_geom)) {
644

    
645
                                        if(!pc.isFollowingLine()){
646
                                                lc.draw(g, labelMetrics, geom);
647
                                        } else {
648
                                                
649
                                                ILabelClass smsLc = new SmartTextSymbolLabelClass();
650
                                                SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(), pc);
651

    
652
                                                double sizeBefore = lc.getTextSymbol().getFont().getSize();
653
                                                double sizeAfter = SymbolUtils.getCartographicLength(this,
654
                                                                sizeBefore,
655
                                                                viewPort,
656
                                                                viewPort.getDPI());
657
                                                sms.setFontSize(sizeAfter);
658

    
659
                                                smsLc.setTextSymbol(sms);
660
                                                geom.transform(viewPort.getAffineTransform());
661
                                                smsLc.draw(g, null, geom);
662
                                                sms.setFontSize(sizeBefore);
663

    
664
                                        }
665
                                        return true;
666
                                }
667
                        } else {
668
                                if(!pc.isFollowingLine()){
669
                                        lc.draw(g, labelMetrics, null);
670
                                }
671
                                else{
672
                                    ILabelClass smsLc = new SmartTextSymbolLabelClass();
673
                                        SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
674

    
675
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
676
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
677
                                                        sizeBefore,
678
                                                        viewPort,
679
                                                        viewPort.getDPI());
680
                                        sms.setFontSize(sizeAfter);
681

    
682
                                        smsLc.setTextSymbol(sms);
683
                                        geom.transform(viewPort.getAffineTransform());
684
                                        smsLc.draw(g, null, geom);
685

    
686
                                        sms.setFontSize(sizeBefore);
687
                                }
688
                                return true;
689
                        }
690
                }
691
                return false;
692
        }
693

    
694
        /**
695
         * Divide una cadena de caracteres por el caracter dos puntos
696
         * siempre que no est? entre comillas.
697
         *
698
         * @param str
699
         *            Cadena de caracteres
700
         *
701
         * @return String[]
702
         *
703
         */
704
        private String[] divideExpression(String str){
705
                ArrayList<String> r = new ArrayList<String>();
706
                boolean inQuotationMarks = false;
707
                int lastIndex = 0;
708
                for(int i=0; i<str.length(); i++){
709
                        if(str.substring(i, i+1).compareTo("\"")==0){
710
                                inQuotationMarks = !inQuotationMarks;
711
                                continue;
712
                        }
713
                        if(str.substring(i, i+1).compareTo(":")==0 && !inQuotationMarks){
714
                                if(lastIndex < i){
715
                                        r.add(str.substring(lastIndex, i));
716
                                }
717
                                lastIndex = i+1;
718
                        }
719
                }
720
                if(lastIndex < str.length()-1){
721
                        r.add(str.substring(lastIndex));
722
                }
723
                String[] result = new String[r.size()];
724
                r.toArray(result);
725
                return result;
726
        }
727

    
728
        /**
729
         * Compute the texts to show in the label and store them in LabelClass.
730
         */
731
        @SuppressWarnings("unchecked")
732
        private boolean setupLabel(Feature featu, ILabelClass lc,
733
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
734
                        double dpi, int duplicateMode){
735

    
736
                String expr = lc.getStringLabelExpression();
737

    
738
                long pt1 = System.currentTimeMillis();
739
                String[] texts = NO_TEXT;
740
                List<String> preTexts = new ArrayList<String>();
741
                try {
742
                        if (expr != null) {
743

    
744
                                if (expr.equals("")) {
745
                                        expr = texts[0];
746
                                }
747

    
748
                                String[] multiexpr = divideExpression(expr);
749
                                for (int i=0; i<multiexpr.length; i++) {
750
                                        
751
                                        expr = multiexpr[i];
752
                                        Object res = LabelClassUtils.evaluate(expr, featu.getEvaluatorData());
753
                                        if (res != null) {
754
                                                preTexts.add(res.toString());
755
                                        } else {
756
                                                preTexts.add("");
757
                                        }
758
                                }
759
                                texts = new String[preTexts.size()];
760
                                preTexts.toArray(texts);
761
//                                parseTime += System.currentTimeMillis()-pt1;
762
                        }
763
                        lc.setTexts(texts);
764

    
765
                } catch (Exception e) {
766
                        logger.warn("While setting up label", e);
767
                        return false;
768
                }
769
                return true;
770
        }
771
        
772
        private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
773
                
774
                if (lblgeom==null || lblgeom.getType() == TYPES.NULL) {
775
                        return false;
776
                }
777
                
778
                Envelope rPixels = lblgeom.getEnvelope();
779
                int minx = (int) rPixels.getMinimum(0);
780
                int miny = (int) rPixels.getMinimum(1);
781
                int maxx = (int) rPixels.getMaximum(0);
782
                int maxy = (int) rPixels.getMaximum(1);
783
                
784
                for (int i = minx; i <= maxx; i++) {
785
                        for (int j = miny; j <= maxy; j++) {
786

    
787
                                if (!lblgeom.contains(i, j)
788
                                                // contains seems to don't detect points
789
                                                // placed in the rectangle boundaries
790
                                                && !lblgeom.intersects(i, j, i, j)) {
791
                                        continue;
792
                                }
793

    
794
                                if (i<0 || j<0) {
795
                                        continue;
796
                                }
797

    
798
                                if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
799
                                        continue;
800
                                }
801

    
802
                                if (bi.getRGB(i,j)!=0){
803
                                        return true;
804
                                }
805
                        }
806
                }
807
                return false;
808
        }
809

    
810
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
811
                
812
                boolean onePoint = false;
813
                int shapeType = geom.getType();
814
                
815
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
816

    
817
                        Envelope env = geom.getEnvelope();
818
                        double dist1Pixel = viewPort.getDist1pixel();
819
                        onePoint = (env.getLength(0) <= dist1Pixel
820
                                        && env.getLength(1) <= dist1Pixel);
821
                }
822
                return onePoint;
823
        }
824

    
825

    
826
        public boolean isAllowingOverlap() {
827
                return allowOverlapping;
828
        }
829

    
830
        public void setAllowOverlapping(boolean allowOverlapping) {
831
                this.allowOverlapping = allowOverlapping;
832
        }
833

    
834
        public IPlacementConstraints getPlacementConstraints() {
835
                if (placementConstraints != null)
836
                        return placementConstraints;
837

    
838
                GeometryType gt = null;
839
                
840
                try {
841
                        gt = layer.getGeometryType();
842
                        // force 2d for comparison
843
                        gt = GeometryLocator.getGeometryManager().getGeometryType(
844
                                        gt.getType(), SUBTYPES.GEOM2D);
845
                } catch (Exception e) {
846
                        logger.error("While getting placements constraints.", e);
847
                        return null;
848
                }
849
                
850
                if (gt.isTypeOf(TYPES.POINT)
851
                                || gt.isTypeOf(TYPES.MULTIPOINT)) {
852
                        return DefaultPointPlacementConstraints;
853
                } else {
854
                        if (gt.isTypeOf(TYPES.CURVE)
855
                                        || gt.isTypeOf(TYPES.MULTICURVE)) {
856
                                return DefaultLinePlacementConstraints;
857
                        } else {
858
                                if (gt.isTypeOf(TYPES.SURFACE)
859
                                                || gt.isTypeOf(TYPES.MULTISURFACE)) {
860
                                        return DefaultPolygonPlacementConstraints;
861
                                } else {
862
                                        if (gt.isTypeOf(TYPES.AGGREGATE) ||
863
                                                        gt.isTypeOf(TYPES.GEOMETRY)) {
864
                                                DefaultMultiShapePlacementConstratints.setPointConstraints(DefaultPointPlacementConstraints);
865
                                                DefaultMultiShapePlacementConstratints.setLineConstraints(DefaultLinePlacementConstraints);
866
                                                DefaultMultiShapePlacementConstratints.setPolygonConstraints(DefaultPolygonPlacementConstraints);
867
                                                return DefaultMultiShapePlacementConstratints;
868
                                        }
869
                                }
870
                        }
871
                }
872
                return null;
873
        }
874

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

    
879
        public IZoomConstraints getZoomConstraints() {
880
                return zoomConstraints;
881
        }
882

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

    
887
        public void print(
888
                        Graphics2D g, double scale, ViewPort viewPort,
889
                        Cancellable cancel, PrintAttributes properties)
890
                        throws ReadException {
891
                
892
                double dpi = 100;
893
                int pq = properties.getPrintQuality();
894
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL){
895
                        dpi = 300;
896
                } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH){
897
                        dpi = 600;
898
                } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT){
899
                        dpi = 72;
900
                }
901

    
902
                viewPort.setOffset(new Point2D.Double(0,0));        
903
                
904
                /* signal printing output */
905
                printMode = true;
906

    
907
                draw(null,g,scale, viewPort,cancel,dpi);
908
        }
909

    
910
        public String[] getUsedFields() {
911

    
912
                /*
913
                 * TODO Solve the problem with the [ and ].
914
                 * Currently SQLJEP evaluator cannot tell which
915
                 * fields are being used. Options: allow [] and remove them or
916
                 * maybe while parsing the SQLJEP evaluator can inform with events like
917
                 * "I have found a field"
918
                 *  
919
                 */
920
                
921
                FeatureAttributeDescriptor[] atts = null;
922
                try {
923
                        atts = layer.getFeatureStore().getDefaultFeatureType().getAttributeDescriptors();
924
                } catch (DataException e) {
925
                        logger.error("While getting atributes.", e);
926
                }
927
                
928
                int n = atts.length;
929
                String[] resp = new String[n];
930
                for (int i=0; i<n; i++) {
931
                        resp[i] = atts[i].getName();
932
                }
933
                return resp;
934

    
935
                /*
936
                ILabelClass[] lcs = method.getLabelClasses();
937
                ArrayList<String> fieldNames = new ArrayList<String>();
938
                for (int i = 0; i < lcs.length; i++) {
939
                        if(lcs[i].getLabelExpressions() != null){
940
                                for (int j = 0; j < lcs[i].getLabelExpressions().length; j++) {
941
                                        String expr = lcs[i].getLabelExpressions()[j];
942
                                        int start;
943
                                        while (expr != null &&
944
                                                        (start = expr.indexOf("[")) != -1) {
945
                                                int end = expr.indexOf("]");
946
                                                String field = expr.substring(start+1, end).trim();
947
                                                if (!fieldNames.contains(field))
948
                                                        fieldNames.add(field);
949
                                                expr = expr.substring(end+1, expr.length());
950
                                        }
951
                                }
952
                        }
953
                }
954
                return fieldNames.toArray(new String[fieldNames.size()]);
955
                */
956
        }
957

    
958

    
959
        public boolean shouldDrawLabels(double scale) {
960
                double minScaleView = -1;
961
                double maxScaleView = -1;
962

    
963
                if (zoomConstraints != null) {
964
                        minScaleView = zoomConstraints.getMinScale();
965
                        maxScaleView = zoomConstraints.getMaxScale();
966
                }
967

    
968
                if (minScaleView == -1 && maxScaleView == -1) {
969
                        // parameters not set, so the layer decides.
970
                        return layer.isWithinScale(scale);
971
                }
972

    
973
                if (minScaleView >= scale) {
974
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
975
                }
976

    
977
                return false;
978
        }
979

    
980
        public void setUnit(int unitIndex) {
981
                unit = unitIndex;
982

    
983
        }
984

    
985
        public int getUnit() {
986
                return unit;
987
        }
988

    
989
        public int getReferenceSystem() {
990
                return referenceSystem;
991
        }
992

    
993
        public void setReferenceSystem(int referenceSystem) {
994
                this.referenceSystem = referenceSystem;
995
        }
996
        
997
        public static void registerPersistent() {
998
                
999
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1000
        if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) {
1001
                DynStruct definition = manager.addDefinition(
1002
                                GeneralLabelingStrategy.class,
1003
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME,
1004
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME+" Persistence definition",
1005
                                null,
1006
                                null);
1007
                definition.addDynFieldObject("labelingMethod")
1008
                .setClassOfValue(ILabelingMethod.class).setMandatory(true);
1009
                definition.addDynFieldObject("placementConstraints")
1010
                .setClassOfValue(IPlacementConstraints.class).setMandatory(false);
1011
                definition.addDynFieldObject("zoomConstraints")
1012
                .setClassOfValue(IZoomConstraints.class).setMandatory(false);
1013
                
1014
                definition.addDynFieldBoolean("allowOverlapping").setMandatory(true);
1015
                definition.addDynFieldInt("unit").setMandatory(true);
1016
                definition.addDynFieldInt("referenceSystem").setMandatory(true);
1017
        }
1018
        }
1019
        
1020
        
1021
        public void loadFromState(PersistentState state) throws PersistenceException {
1022
                
1023
                method = (ILabelingMethod) state.get("labelingMethod");
1024
                
1025
                if (state.hasValue("placementConstraints")) {
1026
                        placementConstraints =
1027
                                        (IPlacementConstraints) state.get("placementConstraints");
1028
                }
1029
                
1030
                if (state.hasValue("zoomConstraints")) {
1031
                        zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
1032
                }
1033

    
1034
                this.allowOverlapping = state.getBoolean("allowOverlapping");
1035
                this.unit = state.getInt("unit");
1036
                this.referenceSystem = state.getInt("referenceSystem");
1037
        }
1038

    
1039
        public void saveToState(PersistentState state) throws PersistenceException {
1040
                
1041
                state.set("labelingMethod", method);
1042
                
1043
                if (placementConstraints != null) {
1044
                        state.set("placementConstraints", placementConstraints);
1045
                }
1046

    
1047
                if (zoomConstraints != null) {
1048
                        state.set("zoomConstraints", zoomConstraints);
1049
                }
1050

    
1051
                state.set("allowOverlapping", allowOverlapping);
1052
                state.set("unit", unit);
1053
                state.set("referenceSystem", referenceSystem);
1054

    
1055
        }
1056

    
1057
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1058
                /*
1059
                 * This method is not used but we must implement CartographicSupport
1060
                 */
1061
                return 0;
1062
        }
1063

    
1064
        public void setCartographicSize(double cartographicSize, Geometry geom) {
1065
                /*
1066
                 * This method is not used but we must implement CartographicSupport
1067
                 */
1068
        }
1069

    
1070
        public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1071
                /*
1072
                 * This method is not used but we must implement CartographicSupport
1073
                 */
1074
                return 0;
1075
        }
1076
        
1077
        public Object clone() throws CloneNotSupportedException {
1078
                return LabelClassUtils.clone(this);
1079
        }
1080

    
1081

    
1082
}