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

History | View | Annotate | Download (32.8 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
                    if (!lc.isVisible()) {
304
                        /*
305
                         * Avoid non-visible labels
306
                         */
307
                        continue;
308
                    }
309
                    
310
                        FeatureSet fset = null;
311
                        
312
                        try {
313
                                fset = method.getFeatureIteratorByLabelClass(
314
                                                layer, lc, viewPort, usedFields);
315
                        } catch (DataException e) {
316
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
317
                        }
318

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

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

    
329
                        boolean bLabelsReallocatable = !isAllowingOverlap();
330

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

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

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

    
368
                        DisposableIterator diter = null;
369
                        try {
370
                                diter = fset.fastIterator();
371
                        } catch (DataException e) {
372
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
373
                        }
374
                        Feature featu = null;
375
                        Geometry geome = null;
376
                        while ( !cancel.isCanceled() && diter.hasNext()) {
377
                                
378
                                featu = (Feature) diter.next();
379
                                geome = featu.getDefaultGeometry();
380
                                if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
381
                                        continue;
382
                                }
383
                                        
384

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

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

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

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

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

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

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

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

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

    
503

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

    
524
        }
525
        
526
        
527
        private void drawLabelInGeom(
528
                        BufferedImage targetBI, Graphics2D targetGr, ILabelClass lc,
529
                        ILabelPlacement placement, ViewPort viewPort, Geometry geom,
530
                        Cancellable cancel,
531
                        double dpi, boolean bLabelsReallocatable) throws GeometryException {
532

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

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

    
545
                /*
546
                 *  Esto provoca errores en el calculo del tama?o de la LabelClass
547
                 * as? que se ha diferido el calculo del tama?o con que deber?a
548
                 * dibujarse el texto justo hasta el momento en que se dibuja ?ste,
549
                 * en el metodo draw de la LabelClass.
550
                 *
551
                 * FIXME: Mantengo el c?digo viejo comentarizado para enfatizar
552
                 * este comentario. Eliminar cuando se asuma que es correcto el cambio.
553
                 *
554
                 */
555

    
556
//                double sizeBefore = lc.getTextSymbol().getFont().getSize();
557

    
558
//                sizeAfter = CartographicSupportToolkit.getCartographicLength(this,
559
//                                sizeBefore,
560
//                                viewPort,
561
//                                MapContext.getScreenDPI());
562

    
563

    
564
                /*
565
                 * search if there is room left by the previous and
566
                 * with more priority labels, then check the current
567
                 * level
568
                 */
569
//                if (
570
                lookupAndPlaceLabel(targetBI, targetGr, llm,
571
                                placement, lc, geom, viewPort, cancel,        bLabelsReallocatable);  //{
572

    
573
//                        lc.getTextSymbol().setFontSize(sizeBefore);
574
//                }
575
//                lc.toCartographicSize(viewPort, dpi, null);
576

    
577
        }
578

    
579
        private int getDuplicateLabelsMode() {
580
                if (getPlacementConstraints() == null) {
581
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
582
                }
583
                return getPlacementConstraints().getDuplicateLabelsMode();
584
        }
585

    
586
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
587
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
588
                        ILabelClass lc, Geometry geom,        ViewPort viewPort,
589
                        Cancellable cancel, boolean bLabelsReallocatable)
590
        throws GeometryException {
591
                
592
                int i;
593
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
594
                        LabelLocationMetrics labelMetrics = llm.get(i);
595

    
596
                        IPlacementConstraints pc = getPlacementConstraints();
597
                        if(pc instanceof MultiShapePlacementConstraints) {
598
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints)pc;
599
                                
600
                                GeometryManager gm = GeometryLocator.getGeometryManager();
601
                                GeometryType line_gt = null;
602
                                GeometryType polyg_gt = null;
603
                                GeometryType geom_gt = null;
604
                                
605
                                line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
606
                                polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
607
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
608

    
609
                                if (geom_gt.getType() == TYPES.POINT ||
610
                                                geom_gt.getType() == TYPES.MULTIPOINT) {
611
                                        pc = mpc.getPointConstraints();
612
                                } else {
613
                                        if (geom_gt.isTypeOf(TYPES.CURVE) ||
614
                                                        geom_gt.getType() == TYPES.MULTICURVE) {
615
                                                pc = mpc.getLineConstraints();
616
                                        } else {
617
                                                if (geom_gt.isTypeOf(TYPES.SURFACE) ||
618
                                                                geom_gt.getType() == TYPES.MULTISURFACE) {
619
                                                        pc = mpc.getPolygonConstraints();
620
                                                }
621
                                        }
622
                                }
623
                        }
624

    
625
                        /*
626
                         * Ver comentario en el metodo drawLabelInGeom
627
                         */
628
//                        lc.getTextSymbol().setFontSize(sizeAfter);// * FConstant.FONT_HEIGHT_SCALE_FACTOR);
629
                        if (bLabelsReallocatable) {
630
                                
631
                                Geometry aux_geom = null;
632
                                aux_geom = lc.getShape(labelMetrics);
633
                                
634
                                if (!isOverlapping(bi, aux_geom)) {
635

    
636
                                        if(!pc.isFollowingLine()){
637
                                                lc.draw(g, labelMetrics, geom);
638
                                        } else {
639
                                                
640
                                                ILabelClass smsLc = new SmartTextSymbolLabelClass();
641
                                                SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(), pc);
642

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

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

    
655
                                        }
656
                                        return true;
657
                                }
658
                        } else {
659
                                if(!pc.isFollowingLine()){
660
                                        lc.draw(g, labelMetrics, null);
661
                                }
662
                                else{
663
                                    ILabelClass smsLc = new SmartTextSymbolLabelClass();
664
                                        SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
665

    
666
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
667
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
668
                                                        sizeBefore,
669
                                                        viewPort,
670
                                                        MapContext.getScreenDPI());
671
                                        sms.setFontSize(sizeAfter);
672

    
673
                                        smsLc.setTextSymbol(sms);
674
                                        geom.transform(viewPort.getAffineTransform());
675
                                        smsLc.draw(g, null, geom);
676

    
677
                                        sms.setFontSize(sizeBefore);
678
                                }
679
                                return true;
680
                        }
681
                }
682
                return false;
683
        }
684

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

    
719
        /**
720
         * Compute the texts to show in the label and store them in LabelClass.
721
         */
722
        @SuppressWarnings("unchecked")
723
        private boolean setupLabel(Feature featu, ILabelClass lc,
724
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
725
                        double dpi, int duplicateMode){//, TreeSet<?> placedLabels) {
726

    
727
//                Value[] vv = feat.getAttributes();
728
                String expr = lc.getStringLabelExpression();
729

    
730
                long pt1 = System.currentTimeMillis();
731
                String[] texts = NO_TEXT;
732
                ArrayList<String> preTexts = new ArrayList<String>();
733
//                String[] texts = {PluginServices.getText(this, "text_field")};
734
                try {
735

    
736
                        for (int i = 0; !cancel.isCanceled() && i < usedFields.length; i++) {
737
                                try {
738
                                        symbol_table.put(usedFields[i], featu.get(usedFields[i]));
739
                                } catch (Exception e) {
740
                                        logger.error("While geatting feature values.", e);
741
                                }
742
                        }
743

    
744
                        if (expr != null) {
745

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

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

    
767
                } catch (Exception e) {
768
                        logger.error("While setting up label", e);
769
                        return false;
770
                }
771
                return true;
772
        }
773
        
774
        private Hashtable<String, Object> symbol_table = new Hashtable<String, Object>();
775

    
776

    
777
        /*
778
        private Expression getEvaluator(String strExpr) {
779
                Expression expr = evaluators.get(strExpr);
780
                if (expr == null) {
781
                        LabelExpressionParser p = new LabelExpressionParser(
782
                                        new StringReader(strExpr),symbol_table);
783

784
                        try {
785
                                p.LabelExpression();
786
                        } catch (ParseException e) {
787
                                logger.error("While getting expression.", e);
788
                        }
789
                        expr = (Expression) p.getStack().pop();
790
                        evaluators.put(strExpr, expr);
791
                }
792
                return expr;
793
        }
794
        */
795

    
796
        private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
797
                
798
                if (lblgeom==null || lblgeom.getType() == TYPES.NULL) {
799
                        return false;
800
                }
801
                
802
                Envelope rPixels = lblgeom.getEnvelope();
803
                int minx = (int) rPixels.getMinimum(0);
804
                int miny = (int) rPixels.getMinimum(1);
805
                int maxx = (int) rPixels.getMaximum(0);
806
                int maxy = (int) rPixels.getMaximum(1);
807
                
808
                for (int i = minx; i <= maxx; i++) {
809
                        for (int j = miny; j <= maxy; j++) {
810

    
811
                                if (!lblgeom.contains(i, j)
812
                                                // contains seems to don't detect points
813
                                                // placed in the rectangle boundaries
814
                                                && !lblgeom.intersects(i, j, i, j)) {
815
                                        continue;
816
                                }
817

    
818
                                if (i<0 || j<0) {
819
                                        continue;
820
                                }
821

    
822
                                if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
823
                                        continue;
824
                                }
825

    
826
                                if (bi.getRGB(i,j)!=0){
827
                                        return true;
828
                                }
829
                        }
830
                }
831
                return false;
832
        }
833

    
834
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
835
                
836
                boolean onePoint = false;
837
                int shapeType = geom.getType();
838
                
839
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
840

    
841
                        Envelope env = geom.getEnvelope();
842
                        double dist1Pixel = viewPort.getDist1pixel();
843
                        onePoint = (env.getLength(0) <= dist1Pixel
844
                                        && env.getLength(1) <= dist1Pixel);
845
                }
846
                return onePoint;
847
        }
848

    
849

    
850
        public boolean isAllowingOverlap() {
851
                return allowOverlapping;
852
        }
853

    
854
        public void setAllowOverlapping(boolean allowOverlapping) {
855
                this.allowOverlapping = allowOverlapping;
856
        }
857

    
858
        public IPlacementConstraints getPlacementConstraints() {
859
                if (placementConstraints != null)
860
                        return placementConstraints;
861

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

    
899
        public void setPlacementConstraints(IPlacementConstraints constraints) {
900
                this.placementConstraints = constraints;
901
        }
902

    
903
        public IZoomConstraints getZoomConstraints() {
904
                return zoomConstraints;
905
        }
906

    
907
        public void setZoomConstraints(IZoomConstraints constraints) {
908
                this.zoomConstraints = constraints;
909
        }
910

    
911
        public void print(
912
                        Graphics2D g, ViewPort viewPort,
913
                        Cancellable cancel, PrintAttributes properties)
914
                        throws ReadException {
915
                
916
                double dpi = 100;
917
                int pq = properties.getPrintQuality();
918
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL){
919
                        dpi = 300;
920
                } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH){
921
                        dpi = 600;
922
                } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT){
923
                        dpi = 72;
924
                }
925

    
926
                viewPort.setOffset(new Point2D.Double(0,0));        
927
                
928
                /* signal printing output */
929
                printMode = true;
930

    
931
                draw(null,g,viewPort,cancel,dpi);
932
        }
933

    
934
        public String[] getUsedFields() {
935

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

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

    
982

    
983
        public boolean shouldDrawLabels(double scale) {
984
                double minScaleView = -1;
985
                double maxScaleView = -1;
986

    
987
                if (zoomConstraints != null) {
988
                        minScaleView = zoomConstraints.getMinScale();
989
                        maxScaleView = zoomConstraints.getMaxScale();
990
                }
991

    
992
                if (minScaleView == -1 && maxScaleView == -1) {
993
                        // parameters not set, so the layer decides.
994
                        return layer.isWithinScale(scale);
995
                }
996

    
997
                if (minScaleView >= scale) {
998
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
999
                }
1000

    
1001
                return false;
1002
        }
1003

    
1004
        public void setUnit(int unitIndex) {
1005
                unit = unitIndex;
1006

    
1007
        }
1008

    
1009
        public int getUnit() {
1010
                return unit;
1011
        }
1012

    
1013
        public int getReferenceSystem() {
1014
                return referenceSystem;
1015
        }
1016

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

    
1058
                this.allowOverlapping = state.getBoolean("allowOverlapping");
1059
                this.unit = state.getInt("unit");
1060
                this.referenceSystem = state.getInt("referenceSystem");
1061
        }
1062

    
1063
        public void saveToState(PersistentState state) throws PersistenceException {
1064
                
1065
                state.set("labelingMethod", method);
1066
                
1067
                if (placementConstraints != null) {
1068
                        state.set("placementConstraints", placementConstraints);
1069
                }
1070

    
1071
                if (zoomConstraints != null) {
1072
                        state.set("zoomConstraints", zoomConstraints);
1073
                }
1074

    
1075
                state.set("allowOverlapping", allowOverlapping);
1076
                state.set("unit", unit);
1077
                state.set("referenceSystem", referenceSystem);
1078

    
1079
        }
1080

    
1081
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1082
                /*
1083
                 * This method is not used but we must implement CartographicSupport
1084
                 */
1085
                return 0;
1086
        }
1087

    
1088
        public void setCartographicSize(double cartographicSize, Geometry geom) {
1089
                /*
1090
                 * This method is not used but we must implement CartographicSupport
1091
                 */
1092
        }
1093

    
1094
        public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1095
                /*
1096
                 * This method is not used but we must implement CartographicSupport
1097
                 */
1098
                return 0;
1099
        }
1100
        
1101
        public Object clone() throws CloneNotSupportedException {
1102
                return LabelClassUtils.clone(this);
1103
        }
1104

    
1105

    
1106
}