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

History | View | Annotate | Download (31.6 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.io.StringReader;
114
import java.util.ArrayList;
115
import java.util.Hashtable;
116
import java.util.Iterator;
117
import java.util.TreeMap;
118
import java.util.TreeSet;
119

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

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

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

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

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

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

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

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

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

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

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

    
265

    
266
                BufferedImage targetBI;
267
                Graphics2D targetGr;
268

    
269

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

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

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

    
282
                /*
283
                 * now we have an ordered set, it is only need to give a pass
284
                 * for each label class to render by priorities.
285
                 *
286
                 * If no priorities were defined, the following loop only executes
287
                 * once
288
                 */
289
                for (ILabelClass lc : ts) {
290
                        
291
                        FeatureSet fset = null;
292
                        
293
                        try {
294
                                fset = method.getFeatureIteratorByLabelClass(layer, lc, viewPort, usedFields);
295
                        } catch (DataException e) {
296
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
297
                        }
298

    
299
                        // duplicates treatment stuff
300
                        /* handle the duplicates mode */
301
                        int duplicateMode = getDuplicateLabelsMode();
302
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
303
                                // we need to register the labels already placed
304

    
305
                                labelsToPlace = new TreeMap<String[], GeometryItem>(
306
                                                new RemoveDuplicatesComparator());
307
                        }
308

    
309
                        boolean bLabelsReallocatable = !isAllowingOverlap();
310

    
311
                        BufferedImage overlapDetectImage = null;
312
                        Graphics2D overlapDetectGraphics = null;
313
                        if (bLabelsReallocatable) {
314
                                int width = viewPort.getImageWidth() + print_offset_x;
315

    
316
                                if(width<0){
317
                                        width = 1;
318
                                }
319
                                int height = viewPort.getImageHeight() + print_offset_y;
320
                                if(height<0){
321
                                        height = 1;
322
                                }
323
                                if (mapImage!=null)
324
                                        overlapDetectImage = new BufferedImage(
325
                                                        mapImage.getWidth()  + print_offset_x,
326
                                                        mapImage.getHeight()  + print_offset_y,
327
                                                        BufferedImage.TYPE_INT_ARGB
328
                                );
329
                                else
330
                                        overlapDetectImage = new BufferedImage(
331
                                                        viewPort.getImageWidth() + print_offset_x,
332
                                                        viewPort.getImageHeight() + print_offset_y,
333
                                                        BufferedImage.TYPE_INT_ARGB
334
                                        );
335

    
336
                                overlapDetectGraphics = overlapDetectImage.createGraphics();
337
                                overlapDetectGraphics.setRenderingHints(mapGraphics.getRenderingHints());
338
                        }
339
                        if (bLabelsReallocatable) {
340
                                targetBI = overlapDetectImage;
341
                                targetGr = overlapDetectGraphics;
342
                                targetGr.translate(-x, -y);
343
                        } else {
344
                                targetBI = mapImage;
345
                                targetGr = mapGraphics;
346
                        }
347

    
348
                        DisposableIterator diter = null;
349
                        try {
350
                                diter = fset.fastIterator();
351
                        } catch (DataException e) {
352
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
353
                        }
354
                        Feature featu = null;
355
                        Geometry geome = null;
356
                        while ( !cancel.isCanceled() && diter.hasNext()) {
357
                                
358
                                featu = (Feature) diter.next();
359
                                geome = featu.getDefaultGeometry();
360
                                if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
361
                                        continue;
362
                                }
363
                                        
364

    
365
                                if (!setupLabel(featu, lc, cancel,
366
                                                usedFields, viewPort, dpi, duplicateMode)) {
367
                                        continue;
368
                                }
369

    
370
                                String[] texts = lc.getTexts();
371

    
372
                                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
373
                                        // check if this text (so label) is already present in the map
374

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

    
398
                                                        Envelope auxBox = geome.getEnvelope();
399
                                                        double perimeterAux = 2 * (auxBox.getLength(0)+auxBox.getLength(1)); 
400
                                                        if (perimeterAux > item.savedPerimeter) {
401
                                                                item.geom = geome; //FConverter.jts_to_igeometry(jtsGeom);
402
                                                                item.savedPerimeter = perimeterAux;
403
                                                        }
404
                                                } else {
405
                                                        int weigh = item.weigh;
406
                                                        
407
                                                        try {
408
                                                                Point pointFromLabel = item.geom.centroid();
409
                                                                Point pointGeome = geome.centroid();
410
                                                                item.geom = GeometryLocator.getGeometryManager().createPoint(
411
                                                                                (pointFromLabel.getX()*weigh + pointGeome.getX())/(weigh+1),
412
                                                                                (pointFromLabel.getY()*weigh + pointGeome.getY())/(weigh+1),
413
                                                                                Geometry.SUBTYPES.GEOM2D);
414
                                                        } catch (Exception ex) {
415
                                                                throw new ReadException(layer.getFeatureStore().getProviderName(), ex);
416
                                                        }
417
                                                        
418
                                                }
419
                                        } else {
420
                                                item.geom = geome;
421
                                        }
422
                                        item.weigh++;
423
                                } else {
424
                                        // Check if size is a pixel
425
                                        if (isOnePoint(viewPort, geome)) {
426
                                                continue;
427
                                        }
428
//                                        lc.toCartographicSize(viewPort, dpi, null);
429

    
430
                                        try {
431
                                                drawLabelInGeom(
432
                                                                targetBI, targetGr,
433
                                                                lc, placement,
434
                                                                viewPort, geome, cancel, dpi,
435
                                                                bLabelsReallocatable);
436
                                        } catch (GeometryException e) {
437
                                                throw new ReadException(
438
                                                                layer.getFeatureStore().getProviderName(), e);
439
                                        }
440
                                        placedCount++;
441
                                }
442
                        }
443
                        
444
                        // ======= End iteration in feature set ====================
445
                        
446
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
447
                                Iterator<String[]> textsIt = labelsToPlace.keySet().iterator();
448
                                while ( !cancel.isCanceled() && textsIt.hasNext()) {
449
                                        notPlacedCount++;
450
                                        String[] texts = textsIt.next();
451

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

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

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

    
483

    
484
                } // big iteration
485
//                double totalTime = (System.currentTimeMillis()-t1);
486
//
487
//                int total = placedCount+notPlacedCount;
488
//
489
//                if (total>0)
490
//                Logger.getLogger(getClass()).info("Labeled layer '"+layer.getName()+
491
//                "' "+totalTime/1000D+" seconds. "+placedCount+"/"+total+
492
//                " labels placed ("+NumberFormat.getInstance().
493
//                format(100*placedCount/(double) total)+"%)");
494
//
495
//                if (cancel.isCanceled()) {
496
//                        Logger.getLogger(getClass()).info("Layer labeling canceled: '"+
497
//                                        layer.getName()+"'");
498
//                } else {
499
//                        Logger.getLogger(getClass()).info("Total labels parse time = "+
500
//                                        parseTime+" ("+NumberFormat.getInstance().
501
//                                        format(parseTime*100/totalTime)+"%)");
502
//                }
503

    
504
        }
505
        
506
        
507
        private void drawLabelInGeom(
508
                        BufferedImage targetBI, Graphics2D targetGr, ILabelClass lc,
509
                        ILabelPlacement placement, ViewPort viewPort, Geometry geom,
510
                        Cancellable cancel,
511
                        double dpi, boolean bLabelsReallocatable) throws GeometryException {
512

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

    
522
                setReferenceSystem(lc.getReferenceSystem());
523
                setUnit(lc.getUnit());
524

    
525
                /*
526
                 *  Esto provoca errores en el calculo del tama?o de la LabelClass
527
                 * as? que se ha diferido el calculo del tama?o con que deber?a
528
                 * dibujarse el texto justo hasta el momento en que se dibuja ?ste,
529
                 * en el metodo draw de la LabelClass.
530
                 *
531
                 * FIXME: Mantengo el c?digo viejo comentarizado para enfatizar
532
                 * este comentario. Eliminar cuando se asuma que es correcto el cambio.
533
                 *
534
                 */
535

    
536
//                double sizeBefore = lc.getTextSymbol().getFont().getSize();
537

    
538
//                sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
539
//                                sizeBefore,
540
//                                viewPort,
541
//                                MapContext.getScreenDPI());
542

    
543

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

    
553
//                        lc.getTextSymbol().setFontSize(sizeBefore);
554
//                }
555
//                lc.toCartographicSize(viewPort, dpi, null);
556

    
557
        }
558

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

    
566
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
567
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
568
                        ILabelClass lc, Geometry geom,        ViewPort viewPort,
569
                        Cancellable cancel, boolean bLabelsReallocatable)
570
        throws GeometryException {
571
                
572
                int i;
573
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
574
                        LabelLocationMetrics labelMetrics = llm.get(i);
575

    
576
                        IPlacementConstraints pc = getPlacementConstraints();
577
                        if(pc instanceof MultiShapePlacementConstraints) {
578
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints)pc;
579
                                
580
                                GeometryManager gm = GeometryLocator.getGeometryManager();
581
                                GeometryType line_gt = null;
582
                                GeometryType polyg_gt = null;
583
                                GeometryType geom_gt = null;
584
                                
585
                                line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
586
                                polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
587
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
588

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

    
605
                        /*
606
                         * Ver comentario en el metodo drawLabelInGeom
607
                         */
608
//                        lc.getTextSymbol().setFontSize(sizeAfter);// * FConstant.FONT_HEIGHT_SCALE_FACTOR);
609
                        if (bLabelsReallocatable) {
610
                                
611
                                Geometry aux_geom = null;
612
                                aux_geom = lc.getShape(labelMetrics);
613
                                
614
                                if (!isOverlapping(bi, aux_geom)) {
615

    
616
                                        if(!pc.isFollowingLine()){
617
                                                lc.draw(g, labelMetrics, geom);
618
                                        } else {
619
                                                
620
                                                ILabelClass smsLc = SymbologyLocator.getSymbologyManager().createDefaultLabel();
621
                                                SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(), pc);
622

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

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

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

    
646
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
647
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
648
                                                        sizeBefore,
649
                                                        viewPort,
650
                                                        MapContext.getScreenDPI());
651
                                        sms.setFontSize(sizeAfter);
652

    
653
                                        smsLc.setTextSymbol(sms);
654
                                        geom.transform(viewPort.getAffineTransform());
655
                                        smsLc.draw(g, null, geom);
656

    
657
                                        sms.setFontSize(sizeBefore);
658
                                }
659
                                return true;
660
                        }
661
                }
662
                return false;
663
        }
664

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

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

    
707
//                Value[] vv = feat.getAttributes();
708
                String expr = lc.getStringLabelExpression();
709

    
710
                long pt1 = System.currentTimeMillis();
711
                String[] texts = NO_TEXT;
712
                ArrayList<String> preTexts = new ArrayList<String>();
713
//                String[] texts = {PluginServices.getText(this, "text_field")};
714
                try {
715

    
716
                        for (int i = 0; !cancel.isCanceled() && i < usedFields.length; i++) {
717
                                try {
718
                                        symbol_table.put(usedFields[i], featu.get(usedFields[i]));
719
                                } catch (Exception e) {
720
                                        logger.error("While geatting feature values.", e);
721
                                }
722
                        }
723

    
724
                        if (expr != null) {
725

    
726
                                if (expr.equals("")) {
727
                                        expr = texts[0];
728
                                }
729

    
730
                                String[] multiexpr = divideExpression(expr);
731
                                for (int i=0; i<multiexpr.length; i++) {
732
                                        
733
                                        expr = multiexpr[i];
734
                                        Object res = LabelClassUtils.evaluate(expr, featu.getEvaluatorData());
735
                                        if (res != null) {
736
                                                preTexts.add(res.toString());
737
                                        } else {
738
                                                preTexts.add("");
739
                                        }
740
                                }
741
                                texts = new String[preTexts.size()];
742
                                preTexts.toArray(texts);
743
                                parseTime += System.currentTimeMillis()-pt1;
744
                        }
745
                        lc.setTexts(texts);
746

    
747
                } catch (Exception e) {
748
                        logger.error("While setting up label", e);
749
                        return false;
750
                }
751
                return true;
752
        }
753
        
754
        private Hashtable<String, Object> symbol_table = new Hashtable<String, Object>();
755

    
756

    
757
        /*
758
        private Expression getEvaluator(String strExpr) {
759
                Expression expr = evaluators.get(strExpr);
760
                if (expr == null) {
761
                        LabelExpressionParser p = new LabelExpressionParser(
762
                                        new StringReader(strExpr),symbol_table);
763

764
                        try {
765
                                p.LabelExpression();
766
                        } catch (ParseException e) {
767
                                logger.error("While getting expression.", e);
768
                        }
769
                        expr = (Expression) p.getStack().pop();
770
                        evaluators.put(strExpr, expr);
771
                }
772
                return expr;
773
        }
774
        */
775

    
776
        private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
777
                
778
                if (lblgeom==null || lblgeom.getType() == TYPES.NULL) {
779
                        return false;
780
                }
781
                
782
                Envelope rPixels = lblgeom.getEnvelope();
783
                int minx = (int) rPixels.getMinimum(0);
784
                int miny = (int) rPixels.getMinimum(1);
785
                int maxx = (int) rPixels.getMaximum(0);
786
                int maxy = (int) rPixels.getMaximum(1);
787
                
788
                for (int i = minx; i <= maxx; i++) {
789
                        for (int j = miny; j <= maxy; j++) {
790

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

    
798
                                if (i<0 || j<0) {
799
                                        continue;
800
                                }
801

    
802
                                if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
803
                                        continue;
804
                                }
805

    
806
                                if (bi.getRGB(i,j)!=0){
807
                                        return true;
808
                                }
809
                        }
810
                }
811
                return false;
812
        }
813

    
814
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
815
                
816
                boolean onePoint = false;
817
                int shapeType = geom.getType();
818
                
819
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
820

    
821
                        Envelope env = geom.getEnvelope();
822
                        double dist1Pixel = viewPort.getDist1pixel();
823
                        onePoint = (env.getLength(0) <= dist1Pixel
824
                                        && env.getLength(1) <= dist1Pixel);
825
                }
826
                return onePoint;
827
        }
828

    
829

    
830
        public boolean isAllowingOverlap() {
831
                return allowOverlapping;
832
        }
833

    
834
        public void setAllowOverlapping(boolean allowOverlapping) {
835
                this.allowOverlapping = allowOverlapping;
836
        }
837

    
838
        public IPlacementConstraints getPlacementConstraints() {
839
                if (placementConstraints != null)
840
                        return placementConstraints;
841

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

    
879
        public void setPlacementConstraints(IPlacementConstraints constraints) {
880
                this.placementConstraints = constraints;
881
        }
882

    
883
        public IZoomConstraints getZoomConstraints() {
884
                return zoomConstraints;
885
        }
886

    
887
        public void setZoomConstraints(IZoomConstraints constraints) {
888
                this.zoomConstraints = constraints;
889
        }
890

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

    
906
                viewPort.setOffset(new Point2D.Double(0,0));        
907
                
908
                /* signal printing output */
909
                printMode = true;
910

    
911
                draw(null,g,viewPort,cancel,dpi);
912
        }
913

    
914
        public String[] getUsedFields() {
915
                
916
                ILabelClass[] lcs = method.getLabelClasses();
917
                ArrayList<String> fieldNames = new ArrayList<String>();
918
                for (int i = 0; i < lcs.length; i++) {
919
                        if(lcs[i].getLabelExpressions() != null){
920
                                for (int j = 0; j < lcs[i].getLabelExpressions().length; j++) {
921
                                        String expr = lcs[i].getLabelExpressions()[j];
922
                                        int start;
923
                                        while (expr != null &&
924
                                                        (start = expr.indexOf("[")) != -1) {
925
                                                int end = expr.indexOf("]");
926
                                                String field = expr.substring(start+1, end).trim();
927
                                                if (!fieldNames.contains(field))
928
                                                        fieldNames.add(field);
929
                                                expr = expr.substring(end+1, expr.length());
930
                                        }
931
                                }
932
                        }
933
                }
934
                return fieldNames.toArray(new String[fieldNames.size()]);
935
        }
936

    
937

    
938
        public boolean shouldDrawLabels(double scale) {
939
                double minScaleView = -1;
940
                double maxScaleView = -1;
941

    
942
                if (zoomConstraints != null) {
943
                        minScaleView = zoomConstraints.getMinScale();
944
                        maxScaleView = zoomConstraints.getMaxScale();
945
                }
946

    
947
                if (minScaleView == -1 && maxScaleView == -1) {
948
                        // parameters not set, so the layer decides.
949
                        return layer.isWithinScale(scale);
950
                }
951

    
952
                if (minScaleView >= scale) {
953
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
954
                }
955

    
956
                return false;
957
        }
958

    
959
        public void setUnit(int unitIndex) {
960
                unit = unitIndex;
961

    
962
        }
963

    
964
        public int getUnit() {
965
                return unit;
966
        }
967

    
968
        public int getReferenceSystem() {
969
                return referenceSystem;
970
        }
971

    
972
        public void setReferenceSystem(int referenceSystem) {
973
                this.referenceSystem = referenceSystem;
974
        }
975
        
976
        public static void registerPersistent() {
977
                
978
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
979
        if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) {
980
                DynStruct definition = manager.addDefinition(
981
                                GeneralLabelingStrategy.class,
982
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME,
983
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME+" Persistence definition",
984
                                null,
985
                                null);
986
                definition.addDynFieldObject("labelingMethod")
987
                .setClassOfValue(ILabelingMethod.class).setMandatory(true);
988
                definition.addDynFieldObject("placementConstraints")
989
                .setClassOfValue(IPlacementConstraints.class).setMandatory(true);
990
                definition.addDynFieldObject("zoomConstraints")
991
                .setClassOfValue(IZoomConstraints.class).setMandatory(true);
992
                
993
                definition.addDynFieldBoolean("allowOverlapping").setMandatory(true);
994
                definition.addDynFieldInt("unit").setMandatory(true);
995
                definition.addDynFieldInt("referenceSystem").setMandatory(true);
996
        }
997
        }
998
        
999
        
1000
        public void loadFromState(PersistentState state) throws PersistenceException {
1001
                
1002
                method = (ILabelingMethod) state.get("labelingMethod");
1003
                placementConstraints = (IPlacementConstraints) state.get("placementConstraints");
1004
                zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
1005
                
1006
                this.allowOverlapping = state.getBoolean("allowOverlapping");
1007
                this.unit = state.getInt("unit");
1008
                this.referenceSystem = state.getInt("referenceSystem");
1009
        }
1010

    
1011
        public void saveToState(PersistentState state) throws PersistenceException {
1012
                
1013
                state.set("labelingMethod", method);
1014
                state.set("placementConstraints", placementConstraints);
1015
                state.set("zoomConstraints", zoomConstraints);
1016

    
1017
                state.set("allowOverlapping", allowOverlapping);
1018
                state.set("unit", unit);
1019
                state.set("referenceSystem", referenceSystem);
1020

    
1021
        }
1022

    
1023
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1024
                /*
1025
                 * This method is not used but we must implement CartographicSupport
1026
                 */
1027
                return 0;
1028
        }
1029

    
1030
        public void setCartographicSize(double cartographicSize, Geometry geom) {
1031
                /*
1032
                 * This method is not used but we must implement CartographicSupport
1033
                 */
1034
        }
1035

    
1036
        public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1037
                /*
1038
                 * This method is not used but we must implement CartographicSupport
1039
                 */
1040
                return 0;
1041
        }
1042
        
1043
        public Object clone() throws CloneNotSupportedException {
1044
                return LabelClassUtils.clone(this);
1045
        }
1046

    
1047

    
1048
}