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

History | View | Annotate | Download (32.7 KB)

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

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

    
110
import java.awt.Graphics2D;
111
import java.awt.geom.Point2D;
112
import java.awt.image.BufferedImage;
113
import java.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.FeatureAttributeDescriptor;
125
import org.gvsig.fmap.dal.feature.FeatureSet;
126
import org.gvsig.fmap.geom.Geometry;
127
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
128
import org.gvsig.fmap.geom.Geometry.TYPES;
129
import org.gvsig.fmap.geom.GeometryException;
130
import org.gvsig.fmap.geom.GeometryLocator;
131
import org.gvsig.fmap.geom.GeometryManager;
132
import org.gvsig.fmap.geom.primitive.Envelope;
133
import org.gvsig.fmap.geom.primitive.Point;
134
import org.gvsig.fmap.geom.type.GeometryType;
135
import org.gvsig.fmap.mapcontext.MapContext;
136
import org.gvsig.fmap.mapcontext.MapContextLocator;
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.AbstractPlacementConstraints;
149
import org.gvsig.labeling.placements.ILabelPlacement;
150
import org.gvsig.labeling.placements.LinePlacementConstraints;
151
import org.gvsig.labeling.placements.MultiShapePlacementConstraints;
152
import org.gvsig.labeling.placements.PlacementManager;
153
import org.gvsig.labeling.placements.PointPlacementConstraints;
154
import org.gvsig.labeling.placements.PolygonPlacementConstraints;
155
import org.gvsig.labeling.placements.RemoveDuplicatesComparator;
156
import org.gvsig.labeling.symbol.SmartTextSymbol;
157
import org.gvsig.labeling.symbol.SymbolUtils;
158
import org.gvsig.symbology.SymbologyLocator;
159
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.styling.LabelLocationMetrics;
160
import org.gvsig.tools.ToolsLocator;
161
import org.gvsig.tools.dispose.DisposableIterator;
162
import org.gvsig.tools.dynobject.DynStruct;
163
import org.gvsig.tools.persistence.PersistenceManager;
164
import org.gvsig.tools.persistence.PersistentState;
165
import org.gvsig.tools.persistence.exception.PersistenceException;
166
import org.gvsig.tools.task.Cancellable;
167
import org.slf4j.Logger;
168
import org.slf4j.LoggerFactory;
169

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

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

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

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

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

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

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

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

    
262
                TreeMap<String[], GeometryItem> labelsToPlace = null;
263
                parseTime =0;
264
//                long t1 = System.currentTimeMillis();
265
                
266
                String[] usedFields = getUsedFields();
267

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

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

    
277

    
278
                BufferedImage targetBI;
279
                Graphics2D targetGr;
280

    
281

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

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

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

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

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

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

    
322
                        boolean bLabelsReallocatable = !isAllowingOverlap();
323

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

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

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

    
361
                        DisposableIterator diter = null;
362
                        try {
363
                                diter = fset.fastIterator();
364
                        } catch (DataException e) {
365
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
366
                        }
367
                        Feature featu = null;
368
                        Geometry geome = null;
369
                        while ( !cancel.isCanceled() && diter.hasNext()) {
370
                                
371
                                featu = (Feature) diter.next();
372
                                geome = featu.getDefaultGeometry();
373
                                if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
374
                                        continue;
375
                                }
376
                                        
377

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

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

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

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

    
411
                                                        Envelope auxBox = geome.getEnvelope();
412
                                                        double perimeterAux = 2 * (auxBox.getLength(0)+auxBox.getLength(1)); 
413
                                                        if (perimeterAux > item.savedPerimeter) {
414
                                                                item.geom = geome; //FConverter.jts_to_igeometry(jtsGeom);
415
                                                                item.savedPerimeter = perimeterAux;
416
                                                        }
417
                                                } else {
418
                                                        int weigh = item.weigh;
419
                                                        
420
                                                        try {
421
                                                                Point pointFromLabel = item.geom.centroid();
422
                                                                Point pointGeome = geome.centroid();
423
                                                                item.geom = GeometryLocator.getGeometryManager().createPoint(
424
                                                                                (pointFromLabel.getX()*weigh + pointGeome.getX())/(weigh+1),
425
                                                                                (pointFromLabel.getY()*weigh + pointGeome.getY())/(weigh+1),
426
                                                                                Geometry.SUBTYPES.GEOM2D);
427
                                                        } catch (Exception ex) {
428
                                                                throw new ReadException(layer.getFeatureStore().getProviderName(), ex);
429
                                                        }
430
                                                        
431
                                                }
432
                                        } else {
433
                                                item.geom = geome;
434
                                        }
435
                                        item.weigh++;
436
                                } else {
437
                                        // Check if size is a pixel
438
                                        if (isOnePoint(viewPort, geome)) {
439
                                                continue;
440
                                        }
441
//                                        lc.toCartographicSize(viewPort, dpi, null);
442

    
443
                                        try {
444
                                                drawLabelInGeom(
445
                                                                targetBI, targetGr,
446
                                                                lc, placement,
447
                                                                viewPort, geome, cancel, dpi,
448
                                                                bLabelsReallocatable);
449
                                        } catch (GeometryException e) {
450
                                                throw new ReadException(
451
                                                                layer.getFeatureStore().getProviderName(), e);
452
                                        }
453
                                        placedCount++;
454
                                }
455
                        }
456
                        
457
                        // ======= End iteration in feature set ====================
458
                        
459
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
460
                                Iterator<String[]> textsIt = labelsToPlace.keySet().iterator();
461
                                while ( !cancel.isCanceled() && textsIt.hasNext()) {
462
                                        notPlacedCount++;
463
                                        String[] texts = textsIt.next();
464

    
465
                                        GeometryItem item = labelsToPlace.get(texts);
466
                                        if(item != null){
467
                                                lc.setTexts(texts);
468
                                                // Check if size is a pixel
469
                                                if (isOnePoint(viewPort, item.geom)) {
470
                                                        continue;
471
                                                }
472

    
473
//                                                lc.toCartographicSize(viewPort, dpi, null);
474
                                                try {
475
                                                        drawLabelInGeom(targetBI, targetGr,
476
                                                                        lc, placement, viewPort, item.geom,
477
                                                                        cancel, dpi, bLabelsReallocatable);
478
                                                } catch (GeometryException e) {
479
                                                        throw new ReadException(
480
                                                                        layer.getFeatureStore().getProviderName(), e);
481
                                                }
482
                                        }
483
                                }
484
                        }
485

    
486
                        if (bLabelsReallocatable) {
487
                                targetGr.translate(x, y);
488
                                if (mapImage!=null) {
489
                                        // Graphics2D aux = (Graphics2D) mapImage.getGraphics();
490
                                        mapGraphics.drawImage(overlapDetectImage, null, null);
491
                                } else {
492
                                        mapGraphics.drawImage(overlapDetectImage, null, null);
493
                                }
494
                        }
495

    
496

    
497
                } // big iteration
498
//                double totalTime = (System.currentTimeMillis()-t1);
499
//
500
//                int total = placedCount+notPlacedCount;
501
//
502
//                if (total>0)
503
//                Logger.getLogger(getClass()).info("Labeled layer '"+layer.getName()+
504
//                "' "+totalTime/1000D+" seconds. "+placedCount+"/"+total+
505
//                " labels placed ("+NumberFormat.getInstance().
506
//                format(100*placedCount/(double) total)+"%)");
507
//
508
//                if (cancel.isCanceled()) {
509
//                        Logger.getLogger(getClass()).info("Layer labeling canceled: '"+
510
//                                        layer.getName()+"'");
511
//                } else {
512
//                        Logger.getLogger(getClass()).info("Total labels parse time = "+
513
//                                        parseTime+" ("+NumberFormat.getInstance().
514
//                                        format(parseTime*100/totalTime)+"%)");
515
//                }
516

    
517
        }
518
        
519
        
520
        private void drawLabelInGeom(
521
                        BufferedImage targetBI, Graphics2D targetGr, ILabelClass lc,
522
                        ILabelPlacement placement, ViewPort viewPort, Geometry geom,
523
                        Cancellable cancel,
524
                        double dpi, boolean bLabelsReallocatable) throws GeometryException {
525

    
526
                lc.toCartographicSize(viewPort, dpi, null);
527
                ArrayList<LabelLocationMetrics> llm = null;
528
                llm = placement.guess(
529
                                lc,
530
                                geom,
531
                                getPlacementConstraints(),
532
                                0,
533
                                cancel,viewPort);
534

    
535
                setReferenceSystem(lc.getReferenceSystem());
536
                setUnit(lc.getUnit());
537

    
538
                /*
539
                 *  Esto provoca errores en el calculo del tama?o de la LabelClass
540
                 * as? que se ha diferido el calculo del tama?o con que deber?a
541
                 * dibujarse el texto justo hasta el momento en que se dibuja ?ste,
542
                 * en el metodo draw de la LabelClass.
543
                 *
544
                 * FIXME: Mantengo el c?digo viejo comentarizado para enfatizar
545
                 * este comentario. Eliminar cuando se asuma que es correcto el cambio.
546
                 *
547
                 */
548

    
549
//                double sizeBefore = lc.getTextSymbol().getFont().getSize();
550

    
551
//                sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
552
//                                sizeBefore,
553
//                                viewPort,
554
//                                MapContext.getScreenDPI());
555

    
556

    
557
                /*
558
                 * search if there is room left by the previous and
559
                 * with more priority labels, then check the current
560
                 * level
561
                 */
562
//                if (
563
                lookupAndPlaceLabel(targetBI, targetGr, llm,
564
                                placement, lc, geom, viewPort, cancel,        bLabelsReallocatable);  //{
565

    
566
//                        lc.getTextSymbol().setFontSize(sizeBefore);
567
//                }
568
//                lc.toCartographicSize(viewPort, dpi, null);
569

    
570
        }
571

    
572
        private int getDuplicateLabelsMode() {
573
                if (getPlacementConstraints() == null) {
574
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
575
                }
576
                return getPlacementConstraints().getDuplicateLabelsMode();
577
        }
578

    
579
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
580
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
581
                        ILabelClass lc, Geometry geom,        ViewPort viewPort,
582
                        Cancellable cancel, boolean bLabelsReallocatable)
583
        throws GeometryException {
584
                
585
                int i;
586
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
587
                        LabelLocationMetrics labelMetrics = llm.get(i);
588

    
589
                        IPlacementConstraints pc = getPlacementConstraints();
590
                        if(pc instanceof MultiShapePlacementConstraints) {
591
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints)pc;
592
                                
593
                                GeometryManager gm = GeometryLocator.getGeometryManager();
594
                                GeometryType line_gt = null;
595
                                GeometryType polyg_gt = null;
596
                                GeometryType geom_gt = null;
597
                                
598
                                line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
599
                                polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
600
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
601

    
602
                                if (geom_gt.getType() == TYPES.POINT ||
603
                                                geom_gt.getType() == TYPES.MULTIPOINT) {
604
                                        pc = mpc.getPointConstraints();
605
                                } else {
606
                                        if (geom_gt.isTypeOf(TYPES.CURVE) ||
607
                                                        geom_gt.getType() == TYPES.MULTICURVE) {
608
                                                pc = mpc.getLineConstraints();
609
                                        } else {
610
                                                if (geom_gt.isTypeOf(TYPES.SURFACE) ||
611
                                                                geom_gt.getType() == TYPES.MULTISURFACE) {
612
                                                        pc = mpc.getPolygonConstraints();
613
                                                }
614
                                        }
615
                                }
616
                        }
617

    
618
                        /*
619
                         * Ver comentario en el metodo drawLabelInGeom
620
                         */
621
//                        lc.getTextSymbol().setFontSize(sizeAfter);// * FConstant.FONT_HEIGHT_SCALE_FACTOR);
622
                        if (bLabelsReallocatable) {
623
                                
624
                                Geometry aux_geom = null;
625
                                aux_geom = lc.getShape(labelMetrics);
626
                                
627
                                if (!isOverlapping(bi, aux_geom)) {
628

    
629
                                        if(!pc.isFollowingLine()){
630
                                                lc.draw(g, labelMetrics, geom);
631
                                        } else {
632
                                                
633
                                                ILabelClass smsLc = SymbologyLocator.getSymbologyManager().createDefaultLabel();
634
                                                SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(), pc);
635

    
636
                                                double sizeBefore = lc.getTextSymbol().getFont().getSize();
637
                                                double sizeAfter = SymbolUtils.getCartographicLength(this,
638
                                                                sizeBefore,
639
                                                                viewPort,
640
                                                                MapContext.getScreenDPI());
641
                                                sms.setFontSize(sizeAfter);
642

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

    
648
                                        }
649
                                        return true;
650
                                }
651
                        } else {
652
                                if(!pc.isFollowingLine()){
653
                                        lc.draw(g, labelMetrics, null);
654
                                }
655
                                else{
656
                                        ILabelClass smsLc = SymbologyLocator.getSymbologyManager().createDefaultLabel();
657
                                        SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
658

    
659
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
660
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
661
                                                        sizeBefore,
662
                                                        viewPort,
663
                                                        MapContext.getScreenDPI());
664
                                        sms.setFontSize(sizeAfter);
665

    
666
                                        smsLc.setTextSymbol(sms);
667
                                        geom.transform(viewPort.getAffineTransform());
668
                                        smsLc.draw(g, null, geom);
669

    
670
                                        sms.setFontSize(sizeBefore);
671
                                }
672
                                return true;
673
                        }
674
                }
675
                return false;
676
        }
677

    
678
        /**
679
         * Divide una cadena de caracteres por el caracter dos puntos
680
         * siempre que no est? entre comillas.
681
         *
682
         * @param str
683
         *            Cadena de caracteres
684
         *
685
         * @return String[]
686
         *
687
         */
688
        private String[] divideExpression(String str){
689
                ArrayList<String> r = new ArrayList<String>();
690
                boolean inQuotationMarks = false;
691
                int lastIndex = 0;
692
                for(int i=0; i<str.length(); i++){
693
                        if(str.substring(i, i+1).compareTo("\"")==0){
694
                                inQuotationMarks = !inQuotationMarks;
695
                                continue;
696
                        }
697
                        if(str.substring(i, i+1).compareTo(":")==0 && !inQuotationMarks){
698
                                if(lastIndex < i){
699
                                        r.add(str.substring(lastIndex, i));
700
                                }
701
                                lastIndex = i+1;
702
                        }
703
                }
704
                if(lastIndex < str.length()-1){
705
                        r.add(str.substring(lastIndex));
706
                }
707
                String[] result = new String[r.size()];
708
                r.toArray(result);
709
                return result;
710
        }
711

    
712
        /**
713
         * Compute the texts to show in the label and store them in LabelClass.
714
         */
715
        @SuppressWarnings("unchecked")
716
        private boolean setupLabel(Feature featu, ILabelClass lc,
717
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
718
                        double dpi, int duplicateMode){//, TreeSet<?> placedLabels) {
719

    
720
//                Value[] vv = feat.getAttributes();
721
                String expr = lc.getStringLabelExpression();
722

    
723
                long pt1 = System.currentTimeMillis();
724
                String[] texts = NO_TEXT;
725
                ArrayList<String> preTexts = new ArrayList<String>();
726
//                String[] texts = {PluginServices.getText(this, "text_field")};
727
                try {
728

    
729
                        for (int i = 0; !cancel.isCanceled() && i < usedFields.length; i++) {
730
                                try {
731
                                        symbol_table.put(usedFields[i], featu.get(usedFields[i]));
732
                                } catch (Exception e) {
733
                                        logger.error("While geatting feature values.", e);
734
                                }
735
                        }
736

    
737
                        if (expr != null) {
738

    
739
                                if (expr.equals("")) {
740
                                        expr = texts[0];
741
                                }
742

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

    
760
                } catch (Exception e) {
761
                        logger.error("While setting up label", e);
762
                        return false;
763
                }
764
                return true;
765
        }
766
        
767
        private Hashtable<String, Object> symbol_table = new Hashtable<String, Object>();
768

    
769

    
770
        /*
771
        private Expression getEvaluator(String strExpr) {
772
                Expression expr = evaluators.get(strExpr);
773
                if (expr == null) {
774
                        LabelExpressionParser p = new LabelExpressionParser(
775
                                        new StringReader(strExpr),symbol_table);
776

777
                        try {
778
                                p.LabelExpression();
779
                        } catch (ParseException e) {
780
                                logger.error("While getting expression.", e);
781
                        }
782
                        expr = (Expression) p.getStack().pop();
783
                        evaluators.put(strExpr, expr);
784
                }
785
                return expr;
786
        }
787
        */
788

    
789
        private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
790
                
791
                if (lblgeom==null || lblgeom.getType() == TYPES.NULL) {
792
                        return false;
793
                }
794
                
795
                Envelope rPixels = lblgeom.getEnvelope();
796
                int minx = (int) rPixels.getMinimum(0);
797
                int miny = (int) rPixels.getMinimum(1);
798
                int maxx = (int) rPixels.getMaximum(0);
799
                int maxy = (int) rPixels.getMaximum(1);
800
                
801
                for (int i = minx; i <= maxx; i++) {
802
                        for (int j = miny; j <= maxy; j++) {
803

    
804
                                if (!lblgeom.contains(i, j)
805
                                                // contains seems to don't detect points
806
                                                // placed in the rectangle boundaries
807
                                                && !lblgeom.intersects(i, j, i, j)) {
808
                                        continue;
809
                                }
810

    
811
                                if (i<0 || j<0) {
812
                                        continue;
813
                                }
814

    
815
                                if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
816
                                        continue;
817
                                }
818

    
819
                                if (bi.getRGB(i,j)!=0){
820
                                        return true;
821
                                }
822
                        }
823
                }
824
                return false;
825
        }
826

    
827
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
828
                
829
                boolean onePoint = false;
830
                int shapeType = geom.getType();
831
                
832
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
833

    
834
                        Envelope env = geom.getEnvelope();
835
                        double dist1Pixel = viewPort.getDist1pixel();
836
                        onePoint = (env.getLength(0) <= dist1Pixel
837
                                        && env.getLength(1) <= dist1Pixel);
838
                }
839
                return onePoint;
840
        }
841

    
842

    
843
        public boolean isAllowingOverlap() {
844
                return allowOverlapping;
845
        }
846

    
847
        public void setAllowOverlapping(boolean allowOverlapping) {
848
                this.allowOverlapping = allowOverlapping;
849
        }
850

    
851
        public IPlacementConstraints getPlacementConstraints() {
852
                if (placementConstraints != null)
853
                        return placementConstraints;
854

    
855
                GeometryType gt = null;
856
                
857
                try {
858
                        gt = layer.getGeometryType();
859
                        // force 2d for comparison
860
                        gt = GeometryLocator.getGeometryManager().getGeometryType(
861
                                        gt.getType(), SUBTYPES.GEOM2D);
862
                } catch (Exception e) {
863
                        logger.error("While getting placements constraints.", e);
864
                        return null;
865
                }
866
                
867
                if (gt.isTypeOf(TYPES.POINT)
868
                                || gt.isTypeOf(TYPES.MULTIPOINT)) {
869
                        return DefaultPointPlacementConstraints;
870
                } else {
871
                        if (gt.isTypeOf(TYPES.CURVE)
872
                                        || gt.isTypeOf(TYPES.MULTICURVE)) {
873
                                return DefaultLinePlacementConstraints;
874
                        } else {
875
                                if (gt.isTypeOf(TYPES.SURFACE)
876
                                                || gt.isTypeOf(TYPES.MULTISURFACE)) {
877
                                        return DefaultPolygonPlacementConstraints;
878
                                } else {
879
                                        if (gt.isTypeOf(TYPES.AGGREGATE) ||
880
                                                        gt.isTypeOf(TYPES.GEOMETRY)) {
881
                                                DefaultMultiShapePlacementConstratints.setPointConstraints(DefaultPointPlacementConstraints);
882
                                                DefaultMultiShapePlacementConstratints.setLineConstraints(DefaultLinePlacementConstraints);
883
                                                DefaultMultiShapePlacementConstratints.setPolygonConstraints(DefaultPolygonPlacementConstraints);
884
                                                return DefaultMultiShapePlacementConstratints;
885
                                        }
886
                                }
887
                        }
888
                }
889
                return null;
890
        }
891

    
892
        public void setPlacementConstraints(IPlacementConstraints constraints) {
893
                this.placementConstraints = constraints;
894
        }
895

    
896
        public IZoomConstraints getZoomConstraints() {
897
                return zoomConstraints;
898
        }
899

    
900
        public void setZoomConstraints(IZoomConstraints constraints) {
901
                this.zoomConstraints = constraints;
902
        }
903

    
904
        public void print(
905
                        Graphics2D g, ViewPort viewPort,
906
                        Cancellable cancel, PrintAttributes properties)
907
                        throws ReadException {
908
                
909
                double dpi = 100;
910
                int pq = properties.getPrintQuality();
911
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL){
912
                        dpi = 300;
913
                } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH){
914
                        dpi = 600;
915
                } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT){
916
                        dpi = 72;
917
                }
918

    
919
                viewPort.setOffset(new Point2D.Double(0,0));        
920
                
921
                /* signal printing output */
922
                printMode = true;
923

    
924
                draw(null,g,viewPort,cancel,dpi);
925
        }
926

    
927
        public String[] getUsedFields() {
928

    
929
                /*
930
                 * TODO Solve the problem with the [ and ].
931
                 * Currently SQLJEP evaluator cannot tell which
932
                 * fields are being used. Options: allow [] and remove them or
933
                 * maybe while parsing the SQLJEP evaluator can inform with events like
934
                 * "I have found a field"
935
                 *  
936
                 */
937
                
938
                FeatureAttributeDescriptor[] atts = null;
939
                try {
940
                        atts = layer.getFeatureStore().getDefaultFeatureType().getAttributeDescriptors();
941
                } catch (DataException e) {
942
                        logger.error("While getting atributes.", e);
943
                }
944
                
945
                int n = atts.length;
946
                String[] resp = new String[n];
947
                for (int i=0; i<n; i++) {
948
                        resp[i] = atts[i].getName();
949
                }
950
                return resp;
951

    
952
                /*
953
                ILabelClass[] lcs = method.getLabelClasses();
954
                ArrayList<String> fieldNames = new ArrayList<String>();
955
                for (int i = 0; i < lcs.length; i++) {
956
                        if(lcs[i].getLabelExpressions() != null){
957
                                for (int j = 0; j < lcs[i].getLabelExpressions().length; j++) {
958
                                        String expr = lcs[i].getLabelExpressions()[j];
959
                                        int start;
960
                                        while (expr != null &&
961
                                                        (start = expr.indexOf("[")) != -1) {
962
                                                int end = expr.indexOf("]");
963
                                                String field = expr.substring(start+1, end).trim();
964
                                                if (!fieldNames.contains(field))
965
                                                        fieldNames.add(field);
966
                                                expr = expr.substring(end+1, expr.length());
967
                                        }
968
                                }
969
                        }
970
                }
971
                return fieldNames.toArray(new String[fieldNames.size()]);
972
                */
973
        }
974

    
975

    
976
        public boolean shouldDrawLabels(double scale) {
977
                double minScaleView = -1;
978
                double maxScaleView = -1;
979

    
980
                if (zoomConstraints != null) {
981
                        minScaleView = zoomConstraints.getMinScale();
982
                        maxScaleView = zoomConstraints.getMaxScale();
983
                }
984

    
985
                if (minScaleView == -1 && maxScaleView == -1) {
986
                        // parameters not set, so the layer decides.
987
                        return layer.isWithinScale(scale);
988
                }
989

    
990
                if (minScaleView >= scale) {
991
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
992
                }
993

    
994
                return false;
995
        }
996

    
997
        public void setUnit(int unitIndex) {
998
                unit = unitIndex;
999

    
1000
        }
1001

    
1002
        public int getUnit() {
1003
                return unit;
1004
        }
1005

    
1006
        public int getReferenceSystem() {
1007
                return referenceSystem;
1008
        }
1009

    
1010
        public void setReferenceSystem(int referenceSystem) {
1011
                this.referenceSystem = referenceSystem;
1012
        }
1013
        
1014
        public static void registerPersistent() {
1015
                
1016
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1017
        if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) {
1018
                DynStruct definition = manager.addDefinition(
1019
                                GeneralLabelingStrategy.class,
1020
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME,
1021
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME+" Persistence definition",
1022
                                null,
1023
                                null);
1024
                definition.addDynFieldObject("labelingMethod")
1025
                .setClassOfValue(ILabelingMethod.class).setMandatory(true);
1026
                definition.addDynFieldObject("placementConstraints")
1027
                .setClassOfValue(IPlacementConstraints.class).setMandatory(false);
1028
                definition.addDynFieldObject("zoomConstraints")
1029
                .setClassOfValue(IZoomConstraints.class).setMandatory(false);
1030
                
1031
                definition.addDynFieldBoolean("allowOverlapping").setMandatory(true);
1032
                definition.addDynFieldInt("unit").setMandatory(true);
1033
                definition.addDynFieldInt("referenceSystem").setMandatory(true);
1034
        }
1035
        }
1036
        
1037
        
1038
        public void loadFromState(PersistentState state) throws PersistenceException {
1039
                
1040
                method = (ILabelingMethod) state.get("labelingMethod");
1041
                
1042
                if (state.hasValue("placementConstraints")) {
1043
                        placementConstraints =
1044
                                        (IPlacementConstraints) state.get("placementConstraints");
1045
                }
1046
                
1047
                if (state.hasValue("zoomConstraints")) {
1048
                        zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
1049
                }
1050

    
1051
                this.allowOverlapping = state.getBoolean("allowOverlapping");
1052
                this.unit = state.getInt("unit");
1053
                this.referenceSystem = state.getInt("referenceSystem");
1054
        }
1055

    
1056
        public void saveToState(PersistentState state) throws PersistenceException {
1057
                
1058
                state.set("labelingMethod", method);
1059
                
1060
                if (placementConstraints != null) {
1061
                        state.set("placementConstraints", placementConstraints);
1062
                }
1063

    
1064
                if (zoomConstraints != null) {
1065
                        state.set("zoomConstraints", zoomConstraints);
1066
                }
1067

    
1068
                state.set("allowOverlapping", allowOverlapping);
1069
                state.set("unit", unit);
1070
                state.set("referenceSystem", referenceSystem);
1071

    
1072
        }
1073

    
1074
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1075
                /*
1076
                 * This method is not used but we must implement CartographicSupport
1077
                 */
1078
                return 0;
1079
        }
1080

    
1081
        public void setCartographicSize(double cartographicSize, Geometry geom) {
1082
                /*
1083
                 * This method is not used but we must implement CartographicSupport
1084
                 */
1085
        }
1086

    
1087
        public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1088
                /*
1089
                 * This method is not used but we must implement CartographicSupport
1090
                 */
1091
                return 0;
1092
        }
1093
        
1094
        public Object clone() throws CloneNotSupportedException {
1095
                return LabelClassUtils.clone(this);
1096
        }
1097

    
1098

    
1099
}