Statistics
| Revision:

svn-gvsig-desktop / trunk / extensions / extSymbology / src / org / gvsig / symbology / fmap / labeling / GeneralLabelingStrategy.java @ 18863

History | View | Annotate | Download (14.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.symbology.fmap.labeling;
109

    
110
import java.awt.Graphics2D;
111
import java.awt.geom.Rectangle2D;
112
import java.awt.image.BufferedImage;
113
import java.io.CharArrayReader;
114
import java.text.NumberFormat;
115
import java.util.ArrayList;
116
import java.util.TreeSet;
117

    
118
import javax.print.attribute.PrintRequestAttributeSet;
119

    
120
import org.apache.log4j.Logger;
121
import org.cresques.cts.ICoordTrans;
122
import org.gvsig.symbology.fmap.labeling.lang.Symbol;
123
import org.gvsig.symbology.fmap.labeling.parse.LabelExpressionParser;
124
import org.gvsig.symbology.fmap.labeling.parse.ParseException;
125
import org.gvsig.symbology.fmap.labeling.placements.ILabelPlacement;
126

    
127
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
128
import com.hardcode.gdbms.engine.values.Value;
129
import com.iver.cit.gvsig.fmap.ViewPort;
130
import com.iver.cit.gvsig.fmap.core.FShape;
131
import com.iver.cit.gvsig.fmap.core.IFeature;
132
import com.iver.cit.gvsig.fmap.core.IGeometry;
133
import com.iver.cit.gvsig.fmap.core.v02.FConverter;
134
import com.iver.cit.gvsig.fmap.drivers.IFeatureIterator;
135
import com.iver.cit.gvsig.fmap.layers.FLayer;
136
import com.iver.cit.gvsig.fmap.layers.FLyrVect;
137
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.ILabelingMethod;
138
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.ILabelingStrategy;
139
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.IPlacementConstraints;
140
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.IZoomConstraints;
141
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.LabelClass;
142
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.LabelLocationMetrics;
143
import com.iver.cit.gvsig.fmap.rendering.styling.labeling.LabelingFactory;
144
import com.iver.utiles.XMLEntity;
145
import com.iver.utiles.swing.threads.Cancellable;
146

    
147
/**
148
 * 
149
 * GeneralLabelingStrategy.java
150
 *
151
 * 
152
 * @author jaume dominguez faus - jaume.dominguez@iver.es Jan 4, 2008
153
 *
154
 */
155
public class GeneralLabelingStrategy implements ILabelingStrategy {
156
        private static Logger logger = Logger.getLogger(GeneralLabelingStrategy.class);
157
        private ILabelingMethod method;
158
        private IPlacementConstraints placementConstraints;
159
        private FLyrVect layer;
160
        private IZoomConstraints zoomConstraints;
161

    
162
        public void setLayer(FLayer layer) throws ReadDriverException {
163
                FLyrVect l = (FLyrVect) layer;
164
                this.layer = l;
165
        }
166

    
167
        public ILabelingMethod getLabelingMethod() {
168
                return method;
169
        }
170

    
171
        public void setLabelingMethod(ILabelingMethod method) {
172
                this.method = method;
173
        }
174

    
175
        public void draw(BufferedImage mapImage, Graphics2D mapGraphics, ViewPort viewPort,
176
                        Cancellable cancel, double dpi)
177
        throws ReadDriverException {
178
                long t1 = System.currentTimeMillis();
179
                String[] usedFields = getUsedFields();
180
                
181
                int notPlacedCount = 0;
182
                int placedCount = 0;
183
                ILabelPlacement placement = PlacementManager.getPlacement(getPlacementConstraints(), layer.getShapeType());
184
                
185
                
186
                        /* 
187
                         * get an ordered set of the LabelClasses up on the 
188
                         * label priority
189
                         */
190
                        LabelClass[] lcs = method.getLabelClasses();
191
                        TreeSet<LabelClass> ts = new TreeSet<LabelClass>(new LabelClassComparatorByPriority());
192
                        
193
                        for (int i = 0; i < lcs.length; i++) ts.add(lcs[i]);
194
                        
195
                        /* 
196
                         * now we have an ordered set, let's create a vector of  
197
                         * feature iterators
198
                         */
199
                        for (LabelClass lc : ts) {
200
                                IFeatureIterator it =  method.getFeatureIteratorByLabelClass(layer, lc, viewPort, usedFields);
201
                                boolean bLabelsReallocatable = true;
202
                                BufferedImage levelImg = null, 
203
                                bi = new BufferedImage(mapImage.getWidth(),mapImage.getHeight(),BufferedImage.TYPE_INT_ARGB);
204
                                Graphics2D levelGraphics = null,
205
                                gBi = bi.createGraphics();
206

    
207
                                if (bLabelsReallocatable) {
208
                                        levelImg = new BufferedImage(mapImage.getWidth(),mapImage.getHeight(),BufferedImage.TYPE_INT_ARGB);
209
                                        levelGraphics = bi.createGraphics();
210
                                }
211

    
212
                                while ( !cancel.isCanceled() && it.hasNext()) {
213

    
214
                                        IFeature feat = it.next();
215
                                        IGeometry geom = feat.getGeometry();
216

    
217
                                        if (!setupLabel(feat, lc, cancel, usedFields, viewPort, dpi))
218
                                                continue;
219

    
220
                                        // Check if size is a pixel
221
                                        if (isOnePoint(viewPort, geom)) {
222
                                                continue;
223
                                        }
224

    
225
                                        BufferedImage[] targetBis;
226
                                        Graphics2D targetG;
227

    
228
                                        if (method.definesPriorities()) {
229
                                                if (bLabelsReallocatable) {
230
                                                        targetBis = new BufferedImage[] { bi, levelImg } ;
231
                                                        targetG = levelGraphics;
232
                                                } else {
233
                                                        targetBis = new BufferedImage[] { bi } ;
234
                                                        targetG = gBi;
235
                                                }
236
                                        } else {
237
                                                if (bLabelsReallocatable) {
238
                                                        targetBis = new BufferedImage[] { bi };
239
                                                        targetG = gBi;
240
                                                } else {
241
                                                        targetBis = new BufferedImage[] { mapImage };
242
                                                        targetG = mapGraphics;
243
                                                }
244
                                        }
245

    
246
                                        // Place a label
247
                                        ArrayList<LabelLocationMetrics> llm = placement.guess(
248
                                                        lc, 
249
                                                        FConverter.transformToInts(geom, viewPort.getAffineTransform()),
250
                                                        getPlacementConstraints(),
251
                                                        0,
252
                                                        cancel);
253

    
254
                                        /* 
255
                                         * search if there is room left by the previous and
256
                                         * with more priority labels, then check the current
257
                                         * level
258
                                         */
259

    
260
                                        if (lookupAndPlaceLabel(targetBis, targetG,
261
                                                        llm, placement, lc, geom, viewPort, cancel,
262
                                                        bLabelsReallocatable)) {
263
                                                placedCount++;
264
                                        } else {
265
                                                notPlacedCount++;
266
                                        }
267

    
268
                                        if (bLabelsReallocatable) {
269
                                                mapGraphics.drawImage(bi, null, null);
270
                                        }
271
                                }
272
                        }
273
                /*} else {
274
                        
275
                        IFeatureIterator it = layer.getSource().getFeatureIterator(
276
                                        viewPort.getAdjustedExtent(),
277
                                        usedFields,
278
                                        viewPort.getProjection(),
279
                                        true);
280
                        
281
                        boolean bLabelsReallocatable = true;
282
                        
283
                        BufferedImage bi = null;
284
                        Graphics2D gBi = null;
285
                        
286
                        if (bLabelsReallocatable) {
287
                                bi = new BufferedImage(mapImage.getWidth(),mapImage.getHeight(),BufferedImage.TYPE_INT_ARGB);
288
                                gBi = bi.createGraphics();
289
                        }
290
                        while ( !cancel.isCanceled() && it.hasNext()) {
291

292
                                IFeature feat = it.next();
293
                                IGeometry geom = feat.getGeometry();
294
                                LabelClass lc = method.getFeatureIteratorByLabelClass(null, feat);
295
                                
296
                                if (!setupLabel(feat, lc, cancel, usedFields, viewPort, dpi))
297
                                        continue;
298
                                
299
                                // Check if size is a pixel
300
                                if (isOnePoint(viewPort, geom)) {
301
                                        continue;
302
                                }
303

304
                                BufferedImage targetBi;
305
                                Graphics2D targetG;
306

307
                                if (bLabelsReallocatable) {
308
                                        targetBi = bi;
309
                                        targetG = gBi;
310
                                } else {
311
                                        targetBi = mapImage;
312
                                        targetG = mapGraphics;
313
                                }
314
                                
315
                                // Place a label
316
                                ArrayList<LabelLocationMetrics> llm = placement.guess(
317
                                                lc, 
318
                                                FConverter.transformToInts(geom, viewPort.getAffineTransform()),
319
                                                getPlacementConstraints(),
320
                                                0,
321
                                                cancel);
322
                                if (lookupAndPlaceLabel(new BufferedImage[] {targetBi}, targetG,
323
                                                           llm, placement, lc, geom, viewPort, cancel,
324
                                                           bLabelsReallocatable)) {
325
                                        placedCount++;
326
                                } else {
327
                                        notPlacedCount++;
328
                                }
329

330
                                if (bLabelsReallocatable) {
331
                                        mapGraphics.drawImage(bi, null, null);
332
                                }
333
                        }
334
                }*/
335
                int total = placedCount+notPlacedCount;
336
                
337
                logger.info("Labeled layer '"+layer.getName()+"' "+(System.currentTimeMillis()-t1)/1000D+" seconds. "+placedCount+"/"+total+" labels placed ("+NumberFormat.getInstance().format(100*placedCount/(double) total)+"%)");
338
        }
339

    
340
        
341
        private boolean lookupAndPlaceLabel(BufferedImage[] bis, Graphics2D g, ArrayList<LabelLocationMetrics> llm, ILabelPlacement placement, LabelClass lc, IGeometry geom, ViewPort viewPort, Cancellable cancel, boolean bLabelsReallocatable) {
342
                
343

    
344
                int i;
345
                for (i = 0; !cancel.isCanceled() && i < llm.size(); i++) {
346
                        LabelLocationMetrics labelMetrics = llm.get(i);
347

    
348
                        if (bLabelsReallocatable) {
349
                                for (int j = 0; j < bis.length; j++) {
350
                                        BufferedImage bi = bis[j];
351
                                        if (!isOverlapping(bi, lc.getShape(labelMetrics))) {
352
                                                lc.draw(g, labelMetrics);
353
                                                return true;
354
                                        }        
355
                                }
356
                        } else {
357
                                lc.draw(g, labelMetrics);        
358
                                return true;
359
                        }
360
                }
361
                return false;
362
        }
363
        
364
        private boolean setupLabel(IFeature feat, LabelClass lc, Cancellable cancel, String[] usedFields, ViewPort viewPort, double dpi) {
365
                if (true) {
366
                Value[] vv = feat.getAttributes();
367
                String expr = lc.getLabelExpression();
368
                LabelExpressionParser parser = new LabelExpressionParser(
369
                                new CharArrayReader(expr.toCharArray()));
370
                for (int i = 0; !cancel.isCanceled() && i < usedFields.length; i++) {
371
                        parser.putSymbol(
372
                                        Symbol.createVariableSymbolFromValue(usedFields[i], vv[i]));
373
                }
374
                String[] texts;
375
                try {
376
                        texts = parser.LabelExpression();
377

    
378
                        lc.setTexts(texts);
379
                } catch (ParseException e) {
380
                        e.printStackTrace();
381
                        return false;
382
                } 
383
                } else {
384
                        lc.setTexts(new String[0]);
385
                }
386

    
387
                lc.toCartographicSize(viewPort, dpi, null);
388
                return true;
389
        }
390
        
391
        private boolean isOverlapping(BufferedImage bi, FShape labelShape) {
392
                
393
                Rectangle2D rPixels = labelShape.getBounds2D();
394
            for (int i= (int) rPixels.getX(); i<rPixels.getMaxX(); i++){
395
                for (int j= (int) rPixels.getY(); j<rPixels.getMaxY(); j++){
396
                        if (!labelShape.contains(i, j)) {
397
                                continue;
398
                        }
399
                        
400
                        if (i<0 || j<0) {
401
                                continue;
402
                        }
403
                        
404
                        if (bi.getWidth()<i+1 || bi.getHeight()<j+1) {
405
                                continue;
406
                        }
407
                        
408
                    if (bi.getRGB(i,j)!=0){
409
//                            Graphics2D g = bi.createGraphics();
410
//                                g.setColor(Color.RED);
411
//                                g.draw(labelShape);
412
                                return true;
413
                    }
414
                }
415
            }
416
//            Graphics2D g = bi.createGraphics();
417
//                g.setColor(Color.GREEN);
418
//                g.draw(labelShape);
419
            return false;
420
        }
421
        
422
        private boolean isOnePoint(ViewPort viewPort, IGeometry geom) {
423
                boolean onePoint = false;
424
                int shapeType = geom.getGeometryType();
425
                if (shapeType!=FShape.POINT && shapeType!=FShape.MULTIPOINT) {
426

    
427
                        Rectangle2D geomBounds = geom.getBounds2D();
428
                        ICoordTrans ct = layer.getCoordTrans();
429

    
430
                        if (ct!=null) {
431
                                geomBounds = ct.convert(geomBounds);
432
                        }
433

    
434
                        double dist1Pixel = viewPort.getDist1pixel();
435
                        onePoint = (geomBounds.getWidth() <= dist1Pixel
436
                                        && geomBounds.getHeight() <= dist1Pixel);
437
                }
438
                return onePoint;
439
        }
440

    
441
        public String getClassName() {
442
                return getClass().getName();
443
        }
444

    
445
        public XMLEntity getXMLEntity() {
446
                XMLEntity xml = new XMLEntity();
447
                xml.putProperty("className", getClassName());
448
                
449
                if (method!=null) {
450
                        XMLEntity methodEntity = method.getXMLEntity();
451
                        methodEntity.putProperty("id", "LabelingMethod");
452
                        xml.addChild(methodEntity);
453
                }
454
                
455
                if (placementConstraints != null) {
456
                        XMLEntity pcEntity = placementConstraints.getXMLEntity();
457
                        pcEntity.putProperty("id", "PlacementConstraints");
458
                        xml.addChild(pcEntity);
459
                }
460
                
461
                if (zoomConstraints != null) {
462
                        XMLEntity zcEntity = zoomConstraints.getXMLEntity();
463
                        zcEntity.putProperty("id", "ZoomConstraints");
464
                        xml.addChild(zcEntity);
465
                }
466
                return xml;
467
        }
468

    
469
        public void setXMLEntity(XMLEntity xml) {
470
                XMLEntity aux = xml.firstChild("id", "LabelingMethod");
471
                if (aux != null) {
472
                        method = LabelingFactory.createMethodFromXML(aux);
473
                }
474
                
475
                aux = xml.firstChild("id", "PlacementConstraints");
476
                if (aux != null) {
477
                        placementConstraints = LabelingFactory.createPlacementConstraintsFromXML(aux);
478
                }
479
                
480
                aux = xml.firstChild("id", "ZoomConstraints");
481
                if (aux != null) {
482
                        zoomConstraints = LabelingFactory.createZoomConstraintsFromXML(aux);
483
                }
484
        }
485

    
486
        public IPlacementConstraints getPlacementConstraints() {
487
                return placementConstraints;
488
        }
489

    
490
        public void setPlacementConstraints(IPlacementConstraints constraints) {
491
                this.placementConstraints = constraints;
492
        }
493

    
494
        public IZoomConstraints getZoomConstraints() {
495
                return zoomConstraints;
496
        }
497

    
498
        public void setZoomConstraints(IZoomConstraints constraints) {
499
                this.zoomConstraints = constraints;
500
        }
501

    
502
        public void print(Graphics2D g, ViewPort viewPort, Cancellable cancel, PrintRequestAttributeSet properties) throws ReadDriverException {
503

    
504
        }
505

    
506
        public String[] getUsedFields() {
507
                LabelClass[] lcs = method.getLabelClasses();
508
                ArrayList<String> fieldNames = new ArrayList<String>(); 
509
                for (int i = 0; i < lcs.length; i++) {
510
                        String expr = lcs[i].getLabelExpression();
511
                        int start;
512
                        while ((start = expr.indexOf("[")) != -1) {
513
                                int end = expr.indexOf("]"); 
514
                                String field = expr.substring(start+1, end).trim();
515
                                if (!fieldNames.contains(field))
516
                                        fieldNames.add(field);
517
                                expr = expr.substring(end+1, expr.length());
518
                        }
519

    
520
                }
521
                return fieldNames.toArray(new String[fieldNames.size()]);
522
        }
523
        
524
}