Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.plugin / org.gvsig.labeling.app / org.gvsig.labeling.app.mainplugin / src / main / java / org / gvsig / labeling / label / GeneralLabelingStrategy.java @ 41676

History | View | Annotate | Download (32.2 KB)

1 40911 jldominguez
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2005 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41
42
/* CVS MESSAGES:
43
 *
44
 * $Id: GeneralLabelingStrategy.java 13749 2007-09-17 14:16:11Z jaume $
45
 * $Log$
46
 * Revision 1.2  2007-09-17 14:16:11  jaume
47
 * multilayer symbols sizing bug fixed
48
 *
49
 * Revision 1.1  2007/05/22 12:17:41  jaume
50
 * *** empty log message ***
51
 *
52
 * Revision 1.1  2007/05/22 10:05:31  jaume
53
 * *** empty log message ***
54
 *
55
 * Revision 1.10  2007/05/17 09:32:06  jaume
56
 * *** empty log message ***
57
 *
58
 * Revision 1.9  2007/05/09 11:04:58  jaume
59
 * refactored legend hierarchy
60
 *
61
 * Revision 1.8  2007/04/13 11:59:30  jaume
62
 * *** empty log message ***
63
 *
64
 * Revision 1.7  2007/04/12 14:28:43  jaume
65
 * basic labeling support for lines
66
 *
67
 * Revision 1.6  2007/04/11 16:01:08  jaume
68
 * maybe a label placer refactor
69
 *
70
 * Revision 1.5  2007/04/10 16:34:01  jaume
71
 * towards a styled labeling
72
 *
73
 * Revision 1.4  2007/04/02 16:34:56  jaume
74
 * Styled labeling (start commiting)
75
 *
76
 * Revision 1.3  2007/03/28 16:48:01  jaume
77
 * *** empty log message ***
78
 *
79
 * Revision 1.2  2007/03/26 14:40:38  jaume
80
 * added print method (BUT UNIMPLEMENTED)
81
 *
82
 * Revision 1.1  2007/03/20 16:16:20  jaume
83
 * refactored to use ISymbol instead of FSymbol
84
 *
85
 * Revision 1.2  2007/03/09 11:20:57  jaume
86
 * Advanced symbology (start committing)
87
 *
88
 * Revision 1.1  2007/03/09 08:33:43  jaume
89
 * *** empty log message ***
90
 *
91
 * Revision 1.1.2.5  2007/02/21 07:34:08  jaume
92
 * labeling starts working
93
 *
94
 * Revision 1.1.2.4  2007/02/15 16:23:44  jaume
95
 * *** empty log message ***
96
 *
97
 * Revision 1.1.2.3  2007/02/09 07:47:05  jaume
98
 * Isymbol moved
99
 *
100
 * Revision 1.1.2.2  2007/02/02 16:21:24  jaume
101
 * start commiting labeling stuff
102
 *
103
 * Revision 1.1.2.1  2007/02/01 17:46:49  jaume
104
 * *** empty log message ***
105
 *
106
 *
107
 */
108
package org.gvsig.labeling.label;
109
110
import java.awt.Graphics2D;
111
import java.awt.geom.Point2D;
112
import java.awt.image.BufferedImage;
113
import java.util.ArrayList;
114 41421 jjdelcerro
import java.util.HashMap;
115 40911 jldominguez
import java.util.Iterator;
116 41227 jldominguez
import java.util.List;
117 41421 jjdelcerro
import java.util.Map;
118 40911 jldominguez
import java.util.TreeMap;
119
import java.util.TreeSet;
120
121
import org.gvsig.compat.print.PrintAttributes;
122
import org.gvsig.fmap.dal.exception.DataException;
123
import org.gvsig.fmap.dal.exception.ReadException;
124
import org.gvsig.fmap.dal.feature.Feature;
125
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
126
import org.gvsig.fmap.dal.feature.FeatureSet;
127
import org.gvsig.fmap.geom.Geometry;
128
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
129
import org.gvsig.fmap.geom.Geometry.TYPES;
130
import org.gvsig.fmap.geom.GeometryException;
131
import org.gvsig.fmap.geom.GeometryLocator;
132
import org.gvsig.fmap.geom.GeometryManager;
133 41227 jldominguez
import org.gvsig.fmap.geom.aggregate.MultiPrimitive;
134 40911 jldominguez
import org.gvsig.fmap.geom.primitive.Envelope;
135
import org.gvsig.fmap.geom.primitive.Point;
136
import org.gvsig.fmap.geom.type.GeometryType;
137
import org.gvsig.fmap.mapcontext.ViewPort;
138
import org.gvsig.fmap.mapcontext.layers.FLayer;
139
import org.gvsig.fmap.mapcontext.layers.vectorial.FLyrVect;
140
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelClass;
141
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingMethod;
142
import org.gvsig.fmap.mapcontext.rendering.legend.styling.ILabelingStrategy;
143
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IPlacementConstraints;
144
import org.gvsig.fmap.mapcontext.rendering.legend.styling.IZoomConstraints;
145
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
146
import org.gvsig.i18n.Messages;
147
import org.gvsig.labeling.lang.LabelClassUtils;
148
import org.gvsig.labeling.placements.ILabelPlacement;
149
import org.gvsig.labeling.placements.LinePlacementConstraints;
150
import org.gvsig.labeling.placements.MultiShapePlacementConstraints;
151
import org.gvsig.labeling.placements.PlacementManager;
152
import org.gvsig.labeling.placements.PointPlacementConstraints;
153
import org.gvsig.labeling.placements.PolygonPlacementConstraints;
154
import org.gvsig.labeling.placements.RemoveDuplicatesComparator;
155
import org.gvsig.labeling.symbol.SmartTextSymbol;
156
import org.gvsig.labeling.symbol.SymbolUtils;
157
import org.gvsig.symbology.SymbologyLocator;
158
import org.gvsig.symbology.fmap.mapcontext.rendering.legend.styling.LabelLocationMetrics;
159
import org.gvsig.tools.ToolsLocator;
160
import org.gvsig.tools.dispose.DisposableIterator;
161
import org.gvsig.tools.dynobject.DynStruct;
162
import org.gvsig.tools.persistence.PersistenceManager;
163
import org.gvsig.tools.persistence.PersistentState;
164
import org.gvsig.tools.persistence.exception.PersistenceException;
165
import org.gvsig.tools.task.Cancellable;
166
import org.slf4j.Logger;
167
import org.slf4j.LoggerFactory;
168
169
/**
170
 *
171
 * GeneralLabelingStrategy.java
172
 *
173
 *
174
 * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 4, 2008
175
 *
176
 */
177
public class GeneralLabelingStrategy implements
178
ILabelingStrategy, Cloneable, CartographicSupport {
179
180
        private static final Logger logger = LoggerFactory.getLogger(
181
                        GeneralLabelingStrategy.class);
182
183
        public static final String GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME =
184
                        "GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME";
185
186
        public static PointPlacementConstraints DefaultPointPlacementConstraints =
187
                        new PointPlacementConstraints();
188
        public static LinePlacementConstraints DefaultLinePlacementConstraints =
189
                        new LinePlacementConstraints();
190
        public static PolygonPlacementConstraints DefaultPolygonPlacementConstraints =
191
                        new PolygonPlacementConstraints();
192
193
        private static String[] NO_TEXT = { Messages.getText("text_field") };
194
195
        private static MultiShapePlacementConstraints DefaultMultiShapePlacementConstratints =
196
                        new MultiShapePlacementConstraints();
197
198
        private ILabelingMethod method;
199
        private IPlacementConstraints placementConstraints;
200
        private IZoomConstraints zoomConstraints;
201
202
        private boolean allowOverlapping;
203
204
        protected FLyrVect layer;
205
206
207 41421 jjdelcerro
//        private long parseTime;
208 40911 jldominguez
        private int unit;
209
        private int referenceSystem;
210
        // private double sizeAfter;
211
        private boolean printMode = false; /* indicate whether output is for a print product (PDF, PS, ...) */
212
213
        public GeneralLabelingStrategy() {
214
                method =
215
                                SymbologyLocator.getSymbologyManager().createDefaultLabelingMethod();
216
        }
217
218
        public void setLayer(FLayer layer) {
219
                FLyrVect l = (FLyrVect) layer;
220
                this.layer = l;
221
        }
222
223
        public ILabelingMethod getLabelingMethod() {
224
                return method;
225
        }
226
227
        public void setLabelingMethod(ILabelingMethod method) {
228
                this.method = method;
229
        }
230
231
        private class GeometryItem{
232
                public Geometry geom = null;
233
                public int weigh = 0;
234
                public double savedPerimeter;
235
236
                public GeometryItem(Geometry geom, int weigh){
237
                        this.geom = geom;
238
                        this.weigh = weigh;
239
                        this.savedPerimeter = 0;
240
                }
241
        }
242
        public void draw(BufferedImage mapImage, Graphics2D mapGraphics,
243 41676 jjdelcerro
                        double scale, ViewPort viewPort,
244 40911 jldominguez
                        Cancellable cancel,
245
                        double dpi) throws ReadException {
246
247
                int x = (int)viewPort.getOffset().getX();
248
                int y = (int)viewPort.getOffset().getY();
249
//                boolean bVisualFXEnabled = false; // if true, the user can see how the labeling is drawing up
250
251
                //offsets for page generation (PDF, PS, direct printing)
252
                int print_offset_x = x;
253
                int print_offset_y = y;
254
                if (printMode) {
255
                        //for printing, we never offset the labels themselves
256
                        x = 0;
257
                        y = 0;
258
                        printMode = false;
259
                }
260
261
                TreeMap<String[], GeometryItem> labelsToPlace = null;
262 41421 jjdelcerro
//                parseTime =0;
263 40911 jldominguez
//                long t1 = System.currentTimeMillis();
264
265
                String[] usedFields = getUsedFields();
266
267
                int notPlacedCount = 0;
268
                int placedCount = 0;
269
270
                /*
271
                 * Get the label placement solvers according the user's settings
272
                 */
273
                ILabelPlacement placement = PlacementManager.getPlacement(
274
                                getPlacementConstraints(), layer.getShapeType());
275
276
277
                BufferedImage targetBI;
278
                Graphics2D targetGr;
279
280
281
                /*
282
                 * get an ordered set of the LabelClasses up on the
283
                 * label priority
284
                 */
285
                ILabelClass[] lcs = method.getLabelClasses();
286
                TreeSet<ILabelClass> ts = new TreeSet<ILabelClass>(
287
                                new LabelClassComparatorByPriority());
288
289
                for (int i = 0; i < lcs.length; i++) ts.add(lcs[i]);
290
291
                if (ts.size()==0) return;
292
293
                /*
294
                 * now we have an ordered set, it is only need to give a pass
295
                 * for each label class to render by priorities.
296
                 *
297
                 * If no priorities were defined, the following loop only executes
298
                 * once
299
                 */
300
                for (ILabelClass lc : ts) {
301
302 41676 jjdelcerro
                    if (!lc.isVisible(scale)) {
303 41194 jldominguez
                        /*
304
                         * Avoid non-visible labels
305
                         */
306
                        continue;
307
                    }
308
309 40911 jldominguez
                        FeatureSet fset = null;
310
311
                        try {
312
                                fset = method.getFeatureIteratorByLabelClass(
313
                                                layer, lc, viewPort, usedFields);
314
                        } catch (DataException e) {
315
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
316
                        }
317
318
                        // duplicates treatment stuff
319
                        /* handle the duplicates mode */
320
                        int duplicateMode = getDuplicateLabelsMode();
321
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
322
                                // we need to register the labels already placed
323
324
                                labelsToPlace = new TreeMap<String[], GeometryItem>(
325
                                                new RemoveDuplicatesComparator());
326
                        }
327
328
                        boolean bLabelsReallocatable = !isAllowingOverlap();
329
330
                        BufferedImage overlapDetectImage = null;
331
                        Graphics2D overlapDetectGraphics = null;
332
                        if (bLabelsReallocatable) {
333
                                int width = viewPort.getImageWidth() + print_offset_x;
334
335
                                if(width<0){
336
                                        width = 1;
337
                                }
338
                                int height = viewPort.getImageHeight() + print_offset_y;
339
                                if(height<0){
340
                                        height = 1;
341
                                }
342
                                if (mapImage!=null)
343
                                        overlapDetectImage = new BufferedImage(
344
                                                        mapImage.getWidth()  + print_offset_x,
345
                                                        mapImage.getHeight()  + print_offset_y,
346
                                                        BufferedImage.TYPE_INT_ARGB
347
                                );
348
                                else
349
                                        overlapDetectImage = new BufferedImage(
350
                                                        viewPort.getImageWidth() + print_offset_x,
351
                                                        viewPort.getImageHeight() + print_offset_y,
352
                                                        BufferedImage.TYPE_INT_ARGB
353
                                        );
354
355
                                overlapDetectGraphics = overlapDetectImage.createGraphics();
356
                                overlapDetectGraphics.setRenderingHints(mapGraphics.getRenderingHints());
357
                        }
358
                        if (bLabelsReallocatable) {
359
                                targetBI = overlapDetectImage;
360
                                targetGr = overlapDetectGraphics;
361
                                targetGr.translate(-x, -y);
362
                        } else {
363
                                targetBI = mapImage;
364
                                targetGr = mapGraphics;
365
                        }
366
367
                        DisposableIterator diter = null;
368
                        try {
369
                                diter = fset.fastIterator();
370
                        } catch (DataException e) {
371
                                throw new ReadException(layer.getFeatureStore().getProviderName(), e);
372
                        }
373
                        Feature featu = null;
374
                        Geometry geome = null;
375
                        while ( !cancel.isCanceled() && diter.hasNext()) {
376
377
                                featu = (Feature) diter.next();
378
                                geome = featu.getDefaultGeometry();
379
                                if (geome == null || geome.getType() == Geometry.TYPES.NULL) {
380
                                        continue;
381
                                }
382
383
384
                                if (!setupLabel(featu, lc, cancel,
385
                                                usedFields, viewPort, dpi, duplicateMode)) {
386
                                        continue;
387
                                }
388
389
                                String[] texts = lc.getTexts();
390
391
                                if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
392
                                        // check if this text (so label) is already present in the map
393
394
                                        GeometryItem item = labelsToPlace.get(texts);
395
                                        if (item == null){
396
                                                item = new GeometryItem(geome, 0);
397
                                                labelsToPlace.put(texts, item);
398
                                        }
399
                                        if (item.geom != null) {
400
401
                                                notPlacedCount++;
402
                                                if (geome.getType() != Geometry.TYPES.POINT) {
403
                                                        // FJP: Cambiamos la uni?n por una comprobaci?n de longitud, por ejemplo.
404
                                                        // La geometr?a con mayor longitud del bounding box ser? la que etiquetamos.
405
                                                        // Ser? inexacto, pero m?s r?pido. Solo lo queremos para saber qu? entidad etiquetamos
406
                                                        // El problema con la uni?n es que para l?neas va muy mal (no sabes lo que te va
407
                                                        // a etiquetar, y para pol?gonos ser? muy lenta. De todas formas, habr?a que evitar
408
                                                        // la conversi?n al JTS.
409
//                                                        Geometry jtsGeom = item.geom.toJTSGeometry().union(geom.toJTSGeometry());
410
//                                                        if (jtsGeom instanceof LineString) {
411
//                                                                CoordinateSequence cs = ((LineString) (jtsGeom)).getCoordinateSequence();
412
//                                                                CoordinateSequences.reverse(cs);
413
//                                                                jtsGeom = new LineString(cs, null);
414
//                                                        }
415
//                                                        item.geom = FConverter.jts_to_igeometry(jtsGeom);
416
417
                                                        Envelope auxBox = geome.getEnvelope();
418
                                                        double perimeterAux = 2 * (auxBox.getLength(0)+auxBox.getLength(1));
419
                                                        if (perimeterAux > item.savedPerimeter) {
420
                                                                item.geom = geome; //FConverter.jts_to_igeometry(jtsGeom);
421
                                                                item.savedPerimeter = perimeterAux;
422
                                                        }
423
                                                } else {
424
                                                        int weigh = item.weigh;
425
426
                                                        try {
427
                                                                Point pointFromLabel = item.geom.centroid();
428
                                                                Point pointGeome = geome.centroid();
429
                                                                item.geom = GeometryLocator.getGeometryManager().createPoint(
430
                                                                                (pointFromLabel.getX()*weigh + pointGeome.getX())/(weigh+1),
431
                                                                                (pointFromLabel.getY()*weigh + pointGeome.getY())/(weigh+1),
432
                                                                                Geometry.SUBTYPES.GEOM2D);
433
                                                        } catch (Exception ex) {
434
                                                                throw new ReadException(layer.getFeatureStore().getProviderName(), ex);
435
                                                        }
436
437
                                                }
438
                                        } else {
439
                                                item.geom = geome;
440
                                        }
441
                                        item.weigh++;
442
                                } else {
443
                                        // Check if size is a pixel
444
                                        if (isOnePoint(viewPort, geome)) {
445
                                                continue;
446
                                        }
447 41227 jldominguez
448
                                        List<Geometry> geome_parts = new ArrayList<Geometry>();
449
                                        if (duplicateMode == IPlacementConstraints.ONE_LABEL_PER_FEATURE_PART) {
450
                                            geome_parts = getGeometryParts(geome);
451
                                        } else {
452
                                            geome_parts.add(geome);
453
                                        }
454 40911 jldominguez
455
                                        try {
456 41227 jldominguez
                                            int n = geome_parts.size();
457
                                            for (int k=0; k<n; k++) {
458
                                drawLabelInGeom(
459
                                targetBI, targetGr,
460
                                lc, placement,
461
                                viewPort, geome_parts.get(k),
462
                                cancel, dpi,
463
                                bLabelsReallocatable);
464
                                            }
465 40911 jldominguez
                                        } catch (GeometryException e) {
466
                                                throw new ReadException(
467
                                                                layer.getFeatureStore().getProviderName(), e);
468
                                        }
469 41227 jldominguez
470
471 40911 jldominguez
                                        placedCount++;
472
                                }
473
                        }
474
475
                        // ======= End iteration in feature set ====================
476
477
                        if (duplicateMode == IPlacementConstraints.REMOVE_DUPLICATE_LABELS) {
478
                                Iterator<String[]> textsIt = labelsToPlace.keySet().iterator();
479
                                while ( !cancel.isCanceled() && textsIt.hasNext()) {
480
                                        notPlacedCount++;
481
                                        String[] texts = textsIt.next();
482
483
                                        GeometryItem item = labelsToPlace.get(texts);
484
                                        if(item != null){
485
                                                lc.setTexts(texts);
486
                                                // Check if size is a pixel
487
                                                if (isOnePoint(viewPort, item.geom)) {
488
                                                        continue;
489
                                                }
490
491
//                                                lc.toCartographicSize(viewPort, dpi, null);
492
                                                try {
493
                                                        drawLabelInGeom(targetBI, targetGr,
494
                                                                        lc, placement, viewPort, item.geom,
495
                                                                        cancel, dpi, bLabelsReallocatable);
496
                                                } catch (GeometryException e) {
497
                                                        throw new ReadException(
498
                                                                        layer.getFeatureStore().getProviderName(), e);
499
                                                }
500
                                        }
501
                                }
502
                        }
503
504
                        if (bLabelsReallocatable) {
505
                                targetGr.translate(x, y);
506
                                if (mapImage!=null) {
507
                                        // Graphics2D aux = (Graphics2D) mapImage.getGraphics();
508
                                        mapGraphics.drawImage(overlapDetectImage, null, null);
509
                                } else {
510
                                        mapGraphics.drawImage(overlapDetectImage, null, null);
511
                                }
512
                        }
513
514
515
                } // big iteration
516
//                double totalTime = (System.currentTimeMillis()-t1);
517
//
518
//                int total = placedCount+notPlacedCount;
519
//
520
//                if (total>0)
521
//                Logger.getLogger(getClass()).info("Labeled layer '"+layer.getName()+
522
//                "' "+totalTime/1000D+" seconds. "+placedCount+"/"+total+
523
//                " labels placed ("+NumberFormat.getInstance().
524
//                format(100*placedCount/(double) total)+"%)");
525
//
526
//                if (cancel.isCanceled()) {
527
//                        Logger.getLogger(getClass()).info("Layer labeling canceled: '"+
528
//                                        layer.getName()+"'");
529
//                } else {
530
//                        Logger.getLogger(getClass()).info("Total labels parse time = "+
531
//                                        parseTime+" ("+NumberFormat.getInstance().
532
//                                        format(parseTime*100/totalTime)+"%)");
533
//                }
534
535
        }
536
537
538 41227 jldominguez
        private List<Geometry> getGeometryParts(Geometry ge) {
539
540
            List<Geometry> resp = new ArrayList<Geometry>();
541
            if (ge != null) {
542
                if (ge instanceof MultiPrimitive) {
543
                    MultiPrimitive mp = (MultiPrimitive) ge;
544
                    int n = mp.getPrimitivesNumber();
545
                    for (int i=0; i<n; i++) {
546
                        resp.add(mp.getPrimitiveAt(i));
547
                    }
548
                } else {
549
                    resp.add(ge);
550
                }
551
            }
552
        return resp;
553
    }
554
555
    private void drawLabelInGeom(
556 40911 jldominguez
                        BufferedImage targetBI, Graphics2D targetGr, ILabelClass lc,
557
                        ILabelPlacement placement, ViewPort viewPort, Geometry geom,
558
                        Cancellable cancel,
559
                        double dpi, boolean bLabelsReallocatable) throws GeometryException {
560
561
                lc.toCartographicSize(viewPort, dpi, null);
562
                ArrayList<LabelLocationMetrics> llm = null;
563
                llm = placement.guess(
564
                                lc,
565
                                geom,
566
                                getPlacementConstraints(),
567
                                0,
568
                                cancel,viewPort);
569
570
                setReferenceSystem(lc.getReferenceSystem());
571
                setUnit(lc.getUnit());
572
573
                /*
574
                 * search if there is room left by the previous and
575
                 * with more priority labels, then check the current
576
                 * level
577
                 */
578
//                if (
579
                lookupAndPlaceLabel(targetBI, targetGr, llm,
580
                                placement, lc, geom, viewPort, cancel,        bLabelsReallocatable);  //{
581
582
//                        lc.getTextSymbol().setFontSize(sizeBefore);
583
//                }
584
//                lc.toCartographicSize(viewPort, dpi, null);
585
586
        }
587
588
        private int getDuplicateLabelsMode() {
589
                if (getPlacementConstraints() == null) {
590
                        return IPlacementConstraints.DefaultDuplicateLabelsMode;
591
                }
592
                return getPlacementConstraints().getDuplicateLabelsMode();
593
        }
594
595
        private boolean lookupAndPlaceLabel(BufferedImage bi, Graphics2D g,
596
                        ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement,
597
                        ILabelClass lc, Geometry geom,        ViewPort viewPort,
598
                        Cancellable cancel, boolean bLabelsReallocatable)
599
        throws GeometryException {
600
601
                int i;
602
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
603
                        LabelLocationMetrics labelMetrics = llm.get(i);
604
605
                        IPlacementConstraints pc = getPlacementConstraints();
606
                        if(pc instanceof MultiShapePlacementConstraints) {
607
                                MultiShapePlacementConstraints mpc = (MultiShapePlacementConstraints)pc;
608
609
                                GeometryManager gm = GeometryLocator.getGeometryManager();
610
                                GeometryType line_gt = null;
611
                                GeometryType polyg_gt = null;
612
                                GeometryType geom_gt = null;
613
614
                                line_gt = gm.getGeometryType(TYPES.CURVE, SUBTYPES.GEOM2D);
615
                                polyg_gt = gm.getGeometryType(TYPES.SURFACE, SUBTYPES.GEOM2D);
616
                                geom_gt = gm.getGeometryType(geom.getType(), SUBTYPES.GEOM2D);
617
618
                                if (geom_gt.getType() == TYPES.POINT ||
619
                                                geom_gt.getType() == TYPES.MULTIPOINT) {
620
                                        pc = mpc.getPointConstraints();
621
                                } else {
622
                                        if (geom_gt.isTypeOf(TYPES.CURVE) ||
623
                                                        geom_gt.getType() == TYPES.MULTICURVE) {
624
                                                pc = mpc.getLineConstraints();
625
                                        } else {
626
                                                if (geom_gt.isTypeOf(TYPES.SURFACE) ||
627
                                                                geom_gt.getType() == TYPES.MULTISURFACE) {
628
                                                        pc = mpc.getPolygonConstraints();
629
                                                }
630
                                        }
631
                                }
632
                        }
633
634
                        /*
635
                         * Ver comentario en el metodo drawLabelInGeom
636
                         */
637
//                        lc.getTextSymbol().setFontSize(sizeAfter);// * FConstant.FONT_HEIGHT_SCALE_FACTOR);
638
                        if (bLabelsReallocatable) {
639
640
                                Geometry aux_geom = null;
641
                                aux_geom = lc.getShape(labelMetrics);
642
643
                                if (!isOverlapping(bi, aux_geom)) {
644
645
                                        if(!pc.isFollowingLine()){
646
                                                lc.draw(g, labelMetrics, geom);
647
                                        } else {
648
649 41131 jldominguez
                                                ILabelClass smsLc = new SmartTextSymbolLabelClass();
650 40911 jldominguez
                                                SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(), pc);
651
652
                                                double sizeBefore = lc.getTextSymbol().getFont().getSize();
653
                                                double sizeAfter = SymbolUtils.getCartographicLength(this,
654
                                                                sizeBefore,
655
                                                                viewPort,
656 41419 jjdelcerro
                                                                viewPort.getDPI());
657 40911 jldominguez
                                                sms.setFontSize(sizeAfter);
658
659
                                                smsLc.setTextSymbol(sms);
660
                                                geom.transform(viewPort.getAffineTransform());
661
                                                smsLc.draw(g, null, geom);
662
                                                sms.setFontSize(sizeBefore);
663
664
                                        }
665
                                        return true;
666
                                }
667
                        } else {
668
                                if(!pc.isFollowingLine()){
669
                                        lc.draw(g, labelMetrics, null);
670
                                }
671
                                else{
672 41131 jldominguez
                                    ILabelClass smsLc = new SmartTextSymbolLabelClass();
673 40911 jldominguez
                                        SmartTextSymbol sms = new SmartTextSymbol(lc.getTextSymbol(),pc);
674
675
                                        double sizeBefore = lc.getTextSymbol().getFont().getSize();
676
                                        double sizeAfter = SymbolUtils.getCartographicLength(this,
677
                                                        sizeBefore,
678
                                                        viewPort,
679 41419 jjdelcerro
                                                        viewPort.getDPI());
680 40911 jldominguez
                                        sms.setFontSize(sizeAfter);
681
682
                                        smsLc.setTextSymbol(sms);
683
                                        geom.transform(viewPort.getAffineTransform());
684
                                        smsLc.draw(g, null, geom);
685
686
                                        sms.setFontSize(sizeBefore);
687
                                }
688
                                return true;
689
                        }
690
                }
691
                return false;
692
        }
693
694
        /**
695
         * Divide una cadena de caracteres por el caracter dos puntos
696
         * siempre que no est? entre comillas.
697
         *
698
         * @param str
699
         *            Cadena de caracteres
700
         *
701
         * @return String[]
702
         *
703
         */
704
        private String[] divideExpression(String str){
705
                ArrayList<String> r = new ArrayList<String>();
706
                boolean inQuotationMarks = false;
707
                int lastIndex = 0;
708
                for(int i=0; i<str.length(); i++){
709
                        if(str.substring(i, i+1).compareTo("\"")==0){
710
                                inQuotationMarks = !inQuotationMarks;
711
                                continue;
712
                        }
713
                        if(str.substring(i, i+1).compareTo(":")==0 && !inQuotationMarks){
714
                                if(lastIndex < i){
715
                                        r.add(str.substring(lastIndex, i));
716
                                }
717
                                lastIndex = i+1;
718
                        }
719
                }
720
                if(lastIndex < str.length()-1){
721
                        r.add(str.substring(lastIndex));
722
                }
723
                String[] result = new String[r.size()];
724
                r.toArray(result);
725
                return result;
726
        }
727
728
        /**
729
         * Compute the texts to show in the label and store them in LabelClass.
730
         */
731
        @SuppressWarnings("unchecked")
732
        private boolean setupLabel(Feature featu, ILabelClass lc,
733
                        Cancellable cancel, String[] usedFields, ViewPort viewPort,
734 41421 jjdelcerro
                        double dpi, int duplicateMode){
735 40911 jldominguez
736
                String expr = lc.getStringLabelExpression();
737
738
                long pt1 = System.currentTimeMillis();
739
                String[] texts = NO_TEXT;
740 41421 jjdelcerro
                List<String> preTexts = new ArrayList<String>();
741 40911 jldominguez
                try {
742
                        if (expr != null) {
743
744
                                if (expr.equals("")) {
745
                                        expr = texts[0];
746
                                }
747
748
                                String[] multiexpr = divideExpression(expr);
749
                                for (int i=0; i<multiexpr.length; i++) {
750
751
                                        expr = multiexpr[i];
752
                                        Object res = LabelClassUtils.evaluate(expr, featu.getEvaluatorData());
753
                                        if (res != null) {
754
                                                preTexts.add(res.toString());
755
                                        } else {
756 41421 jjdelcerro
                                                preTexts.add("");
757 40911 jldominguez
                                        }
758
                                }
759
                                texts = new String[preTexts.size()];
760
                                preTexts.toArray(texts);
761 41421 jjdelcerro
//                                parseTime += System.currentTimeMillis()-pt1;
762 40911 jldominguez
                        }
763
                        lc.setTexts(texts);
764
765
                } catch (Exception e) {
766 41421 jjdelcerro
                        logger.warn("While setting up label", e);
767 40911 jldominguez
                        return false;
768
                }
769
                return true;
770
        }
771
772
        private boolean isOverlapping(BufferedImage bi, Geometry lblgeom) {
773
774
                if (lblgeom==null || lblgeom.getType() == TYPES.NULL) {
775
                        return false;
776
                }
777
778
                Envelope rPixels = lblgeom.getEnvelope();
779
                int minx = (int) rPixels.getMinimum(0);
780
                int miny = (int) rPixels.getMinimum(1);
781
                int maxx = (int) rPixels.getMaximum(0);
782
                int maxy = (int) rPixels.getMaximum(1);
783
784
                for (int i = minx; i <= maxx; i++) {
785
                        for (int j = miny; j <= maxy; j++) {
786
787
                                if (!lblgeom.contains(i, j)
788
                                                // contains seems to don't detect points
789
                                                // placed in the rectangle boundaries
790
                                                && !lblgeom.intersects(i, j, i, j)) {
791
                                        continue;
792
                                }
793
794
                                if (i<0 || j<0) {
795
                                        continue;
796
                                }
797
798
                                if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
799
                                        continue;
800
                                }
801
802
                                if (bi.getRGB(i,j)!=0){
803
                                        return true;
804
                                }
805
                        }
806
                }
807
                return false;
808
        }
809
810
        private boolean isOnePoint(ViewPort viewPort, Geometry geom) {
811
812
                boolean onePoint = false;
813
                int shapeType = geom.getType();
814
815
                if (shapeType != TYPES.POINT && shapeType != TYPES.MULTIPOINT) {
816
817
                        Envelope env = geom.getEnvelope();
818
                        double dist1Pixel = viewPort.getDist1pixel();
819
                        onePoint = (env.getLength(0) <= dist1Pixel
820
                                        && env.getLength(1) <= dist1Pixel);
821
                }
822
                return onePoint;
823
        }
824
825
826
        public boolean isAllowingOverlap() {
827
                return allowOverlapping;
828
        }
829
830
        public void setAllowOverlapping(boolean allowOverlapping) {
831
                this.allowOverlapping = allowOverlapping;
832
        }
833
834
        public IPlacementConstraints getPlacementConstraints() {
835
                if (placementConstraints != null)
836
                        return placementConstraints;
837
838
                GeometryType gt = null;
839
840
                try {
841
                        gt = layer.getGeometryType();
842
                        // force 2d for comparison
843
                        gt = GeometryLocator.getGeometryManager().getGeometryType(
844
                                        gt.getType(), SUBTYPES.GEOM2D);
845
                } catch (Exception e) {
846
                        logger.error("While getting placements constraints.", e);
847
                        return null;
848
                }
849
850
                if (gt.isTypeOf(TYPES.POINT)
851
                                || gt.isTypeOf(TYPES.MULTIPOINT)) {
852
                        return DefaultPointPlacementConstraints;
853
                } else {
854
                        if (gt.isTypeOf(TYPES.CURVE)
855
                                        || gt.isTypeOf(TYPES.MULTICURVE)) {
856
                                return DefaultLinePlacementConstraints;
857
                        } else {
858
                                if (gt.isTypeOf(TYPES.SURFACE)
859
                                                || gt.isTypeOf(TYPES.MULTISURFACE)) {
860
                                        return DefaultPolygonPlacementConstraints;
861
                                } else {
862
                                        if (gt.isTypeOf(TYPES.AGGREGATE) ||
863
                                                        gt.isTypeOf(TYPES.GEOMETRY)) {
864
                                                DefaultMultiShapePlacementConstratints.setPointConstraints(DefaultPointPlacementConstraints);
865
                                                DefaultMultiShapePlacementConstratints.setLineConstraints(DefaultLinePlacementConstraints);
866
                                                DefaultMultiShapePlacementConstratints.setPolygonConstraints(DefaultPolygonPlacementConstraints);
867
                                                return DefaultMultiShapePlacementConstratints;
868
                                        }
869
                                }
870
                        }
871
                }
872
                return null;
873
        }
874
875
        public void setPlacementConstraints(IPlacementConstraints constraints) {
876
                this.placementConstraints = constraints;
877
        }
878
879
        public IZoomConstraints getZoomConstraints() {
880
                return zoomConstraints;
881
        }
882
883
        public void setZoomConstraints(IZoomConstraints constraints) {
884
                this.zoomConstraints = constraints;
885
        }
886
887
        public void print(
888 41676 jjdelcerro
                        Graphics2D g, double scale, ViewPort viewPort,
889 40911 jldominguez
                        Cancellable cancel, PrintAttributes properties)
890
                        throws ReadException {
891
892
                double dpi = 100;
893
                int pq = properties.getPrintQuality();
894
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL){
895
                        dpi = 300;
896
                } else if (pq == PrintAttributes.PRINT_QUALITY_HIGH){
897
                        dpi = 600;
898
                } else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT){
899
                        dpi = 72;
900
                }
901
902
                viewPort.setOffset(new Point2D.Double(0,0));
903
904
                /* signal printing output */
905
                printMode = true;
906
907 41676 jjdelcerro
                draw(null,g,scale, viewPort,cancel,dpi);
908 40911 jldominguez
        }
909
910
        public String[] getUsedFields() {
911
912
                /*
913
                 * TODO Solve the problem with the [ and ].
914
                 * Currently SQLJEP evaluator cannot tell which
915
                 * fields are being used. Options: allow [] and remove them or
916
                 * maybe while parsing the SQLJEP evaluator can inform with events like
917
                 * "I have found a field"
918
                 *
919
                 */
920
921
                FeatureAttributeDescriptor[] atts = null;
922
                try {
923
                        atts = layer.getFeatureStore().getDefaultFeatureType().getAttributeDescriptors();
924
                } catch (DataException e) {
925
                        logger.error("While getting atributes.", e);
926
                }
927
928
                int n = atts.length;
929
                String[] resp = new String[n];
930
                for (int i=0; i<n; i++) {
931
                        resp[i] = atts[i].getName();
932
                }
933
                return resp;
934
935
                /*
936
                ILabelClass[] lcs = method.getLabelClasses();
937
                ArrayList<String> fieldNames = new ArrayList<String>();
938
                for (int i = 0; i < lcs.length; i++) {
939
                        if(lcs[i].getLabelExpressions() != null){
940
                                for (int j = 0; j < lcs[i].getLabelExpressions().length; j++) {
941
                                        String expr = lcs[i].getLabelExpressions()[j];
942
                                        int start;
943
                                        while (expr != null &&
944
                                                        (start = expr.indexOf("[")) != -1) {
945
                                                int end = expr.indexOf("]");
946
                                                String field = expr.substring(start+1, end).trim();
947
                                                if (!fieldNames.contains(field))
948
                                                        fieldNames.add(field);
949
                                                expr = expr.substring(end+1, expr.length());
950
                                        }
951
                                }
952
                        }
953
                }
954
                return fieldNames.toArray(new String[fieldNames.size()]);
955
                */
956
        }
957
958
959
        public boolean shouldDrawLabels(double scale) {
960
                double minScaleView = -1;
961
                double maxScaleView = -1;
962
963
                if (zoomConstraints != null) {
964
                        minScaleView = zoomConstraints.getMinScale();
965
                        maxScaleView = zoomConstraints.getMaxScale();
966
                }
967
968
                if (minScaleView == -1 && maxScaleView == -1) {
969
                        // parameters not set, so the layer decides.
970
                        return layer.isWithinScale(scale);
971
                }
972
973
                if (minScaleView >= scale) {
974
                        return (maxScaleView != -1) ? maxScaleView <= scale : true;
975
                }
976
977
                return false;
978
        }
979
980
        public void setUnit(int unitIndex) {
981
                unit = unitIndex;
982
983
        }
984
985
        public int getUnit() {
986
                return unit;
987
        }
988
989
        public int getReferenceSystem() {
990
                return referenceSystem;
991
        }
992
993
        public void setReferenceSystem(int referenceSystem) {
994
                this.referenceSystem = referenceSystem;
995
        }
996
997
        public static void registerPersistent() {
998
999
        PersistenceManager manager = ToolsLocator.getPersistenceManager();
1000
        if (manager.getDefinition(GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME) == null) {
1001
                DynStruct definition = manager.addDefinition(
1002
                                GeneralLabelingStrategy.class,
1003
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME,
1004
                                GENERAL_LABEL_STRATEGY_PERSISTENCE_NAME+" Persistence definition",
1005
                                null,
1006
                                null);
1007
                definition.addDynFieldObject("labelingMethod")
1008
                .setClassOfValue(ILabelingMethod.class).setMandatory(true);
1009
                definition.addDynFieldObject("placementConstraints")
1010
                .setClassOfValue(IPlacementConstraints.class).setMandatory(false);
1011
                definition.addDynFieldObject("zoomConstraints")
1012
                .setClassOfValue(IZoomConstraints.class).setMandatory(false);
1013
1014
                definition.addDynFieldBoolean("allowOverlapping").setMandatory(true);
1015
                definition.addDynFieldInt("unit").setMandatory(true);
1016
                definition.addDynFieldInt("referenceSystem").setMandatory(true);
1017
        }
1018
        }
1019
1020
1021
        public void loadFromState(PersistentState state) throws PersistenceException {
1022
1023
                method = (ILabelingMethod) state.get("labelingMethod");
1024
1025
                if (state.hasValue("placementConstraints")) {
1026
                        placementConstraints =
1027
                                        (IPlacementConstraints) state.get("placementConstraints");
1028
                }
1029
1030
                if (state.hasValue("zoomConstraints")) {
1031
                        zoomConstraints = (IZoomConstraints) state.get("zoomConstraints");
1032
                }
1033
1034
                this.allowOverlapping = state.getBoolean("allowOverlapping");
1035
                this.unit = state.getInt("unit");
1036
                this.referenceSystem = state.getInt("referenceSystem");
1037
        }
1038
1039
        public void saveToState(PersistentState state) throws PersistenceException {
1040
1041
                state.set("labelingMethod", method);
1042
1043
                if (placementConstraints != null) {
1044
                        state.set("placementConstraints", placementConstraints);
1045
                }
1046
1047
                if (zoomConstraints != null) {
1048
                        state.set("zoomConstraints", zoomConstraints);
1049
                }
1050
1051
                state.set("allowOverlapping", allowOverlapping);
1052
                state.set("unit", unit);
1053
                state.set("referenceSystem", referenceSystem);
1054
1055
        }
1056
1057
        public double toCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1058
                /*
1059
                 * This method is not used but we must implement CartographicSupport
1060
                 */
1061
                return 0;
1062
        }
1063
1064
        public void setCartographicSize(double cartographicSize, Geometry geom) {
1065
                /*
1066
                 * This method is not used but we must implement CartographicSupport
1067
                 */
1068
        }
1069
1070
        public double getCartographicSize(ViewPort vp, double dpi, Geometry geom) {
1071
                /*
1072
                 * This method is not used but we must implement CartographicSupport
1073
                 */
1074
                return 0;
1075
        }
1076
1077
        public Object clone() throws CloneNotSupportedException {
1078
                return LabelClassUtils.clone(this);
1079
        }
1080
1081
1082
}