Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.library / org.gvsig.symbology / org.gvsig.symbology.lib / org.gvsig.symbology.lib.impl / src / main / java / org / gvsig / symbology / fmap / mapcontext / rendering / symbol / text / impl / SimpleTextSymbol.java @ 41124

History | View | Annotate | Download (15.9 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.symbology.fmap.mapcontext.rendering.symbol.text.impl;
25

    
26
import java.awt.BasicStroke;
27
import java.awt.Color;
28
import java.awt.Font;
29
import java.awt.FontMetrics;
30
import java.awt.Graphics2D;
31
import java.awt.Rectangle;
32
import java.awt.RenderingHints;
33
import java.awt.Shape;
34
import java.awt.font.FontRenderContext;
35
import java.awt.font.GlyphVector;
36
import java.awt.geom.AffineTransform;
37
import java.awt.geom.Rectangle2D;
38

    
39
import org.slf4j.Logger;
40
import org.slf4j.LoggerFactory;
41

    
42
import org.gvsig.compat.print.PrintAttributes;
43
import org.gvsig.fmap.dal.feature.Feature;
44
import org.gvsig.fmap.geom.Geometry;
45
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
46
import org.gvsig.fmap.geom.GeometryLocator;
47
import org.gvsig.fmap.geom.GeometryManager;
48
import org.gvsig.fmap.geom.exception.CreateGeometryException;
49
import org.gvsig.fmap.geom.primitive.GeneralPathX;
50
import org.gvsig.fmap.geom.primitive.Point;
51
import org.gvsig.fmap.mapcontext.MapContextLocator;
52
import org.gvsig.fmap.mapcontext.ViewPort;
53
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
54
import org.gvsig.fmap.mapcontext.rendering.symbols.ITextSymbol;
55
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolDrawingException;
56
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
57
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolPreferences;
58
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.AbstractSymbol;
59
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.CartographicSupportToolkit;
60
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.text.ISimpleTextSymbol;
61
import org.gvsig.tools.ToolsLocator;
62
import org.gvsig.tools.dynobject.DynStruct;
63
import org.gvsig.tools.persistence.PersistenceManager;
64
import org.gvsig.tools.persistence.PersistentState;
65
import org.gvsig.tools.persistence.exception.PersistenceException;
66
import org.gvsig.tools.task.Cancellable;
67
import org.gvsig.tools.util.Callable;
68

    
69
/**
70
 * SimpleTextSymbol is a class used to create symbols composed using a text defined by
71
 * the user.This text can be edited (changing the color, the font of the characters, and
72
 * the rotation of the text).
73
 * @author 2005-2008 jaume dominguez faus - jaume.dominguez@iver.es
74
 * @author 2009-     <a href="cordinyana@gvsig.org">C?sar Ordi?ana</a> - gvSIG team
75
 */
76
public class SimpleTextSymbol extends AbstractSymbol implements ITextSymbol, ISimpleTextSymbol {
77
        final static private Logger LOG = LoggerFactory.getLogger(SimpleTextSymbol.class);
78

    
79
        public static final String SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME = "SimpleTextSymbol";
80
        
81
        private static final String FIELD_TEXT = "text";
82
        private static final String FIELD_FONT = "font";
83
        private static final String FIELD_TEXT_COLOR = "textColor";
84
        private static final String FIELD_ROTATION = "rotation";
85
        private static final String FIELD_AUTO_RESIZE = "autoResize";
86
        
87
        private static final String FIELD_HAS_HALO = "hasHalo";
88
        private static final String FIELD_HALO_COLOR = "haloColor";
89
        private static final String FIELD_HALO_WIDTH = "haloWidth";
90

    
91
        private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
92
        
93
        /*
94
         * This value must be get using getText() if the 'null'
95
         * value has to prevented
96
         */
97
        private String text = "";
98
        private Font font;
99
        
100
        private Color haloColor = Color.WHITE;
101
        private float haloWidth = 3;
102
        private boolean drawWithHalo = false;
103
        private BasicStroke haloStroke = new BasicStroke(
104
                        6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
105

    
106
        private Color textColor;
107
        private double rotation;
108
        private FontRenderContext frc = new FontRenderContext(
109
                        new AffineTransform(), false, true);
110
        private boolean autoresize;
111

    
112
        public SimpleTextSymbol() {
113
                super();
114
                SymbolPreferences preferences =
115
                                MapContextLocator.getSymbolManager().getSymbolPreferences();
116
                font = preferences.getDefaultSymbolFont();
117
                textColor = preferences.getDefaultSymbolColor();
118
        }
119

    
120
        public void draw(Graphics2D g, AffineTransform affineTransform,
121
                        Geometry geom, Feature feature, Cancellable cancel) {
122
                if (!isShapeVisible()) {
123
                        return;
124
                }
125
                
126
                Point point = (Point)geom;
127
            point.transform(affineTransform);
128
          
129
                g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
130
                g.translate(point.getX(), point.getY());
131

    
132
                g.rotate(rotation);
133
                
134
                if (isDrawWithHalo()) {
135
                        char[] charText = new char[getText().length()];
136
                        getText().getChars(0, charText.length, charText, 0);
137
                        GlyphVector glyph = font.layoutGlyphVector(frc, charText, 0, charText.length, Font.LAYOUT_NO_LIMIT_CONTEXT);
138
                        g.setColor(getHaloColor());
139
                        g.setStroke(haloStroke);
140
                        g.draw(glyph.getOutline());
141
                }
142
                // Rectangle2D bounds = getBounds();
143
//                 getBounds devuelve el bounds de un texto dibujado de manera que
144
                // la linea base de la primera letra est? en el punto (0,0).
145
                // Por eso, si queremos alinear el texto de manera que la parte superior
146
                // izquierda de la primera letra est? en (0,0) debemos moverlo seg?n
147
                // el valor de la ordenada, en lugar de seg?n su altura.
148
                g.setColor(textColor);
149
                g.setFont(font);
150

    
151
                g.drawString(getText(), 0, 0);//(int) bounds.getHeight());
152
                g.rotate(-rotation);
153
                g.translate(-point.getX(), -point.getY());
154
        }
155

    
156
        public void drawInsideRectangle(Graphics2D g,
157
                        AffineTransform scaleInstance, Rectangle r, PrintAttributes properties) throws SymbolDrawingException {
158
                int s = getFont().getSize();
159
                if (autoresize) {
160
                        if (s==0) {
161
                                s =1;
162
                                setFontSize(s);
163
                        }
164
                        g.setFont(getFont());
165
                    FontMetrics fm = g.getFontMetrics();
166
                    Rectangle2D rect = fm.getStringBounds(getText(), g);
167
                    double width = rect.getWidth();
168
                    double height = rect.getHeight();
169
                    double rWidth = r.getWidth();
170
                    double rHeight = r.getHeight();
171
                    double ratioText = width/height;
172
                    double ratioRect = rWidth/rHeight;
173

    
174
                    if (ratioText>ratioRect) {
175
                            s = (int) (s*(rWidth/width));
176
                    } else {
177
                            s = (int) (s*(rHeight/height));
178
                    }
179
//                    r.setLocation(r.x, (int) Math.round(r.getY()+r.getHeight()));
180
                }
181
                setFontSize(s);
182
//                g.setColor(Color.red);
183
//                g.draw(r);
184
                try {
185
                        if (properties==null)
186
                                draw(g, null, geomManager.createPoint(
187
                                                r.getX(),
188
                                                // Small adjustment to center the fonts
189
                                                r.getY() + 0.725 * r.getHeight(),
190
                                                SUBTYPES.GEOM2D), null, null);
191
                        else
192
                                print(g, new AffineTransform(), geomManager.createPoint(r.getX(), r.getY(), SUBTYPES.GEOM2D), properties);
193
                } catch (CreateGeometryException e) {
194
                        LOG.error("Error creatint a point", e);
195
                        e.printStackTrace();
196
                }
197

    
198

    
199
        }
200

    
201
        public int getOnePointRgb() {
202
                return textColor.getRGB();
203
        }
204

    
205
        public void getPixExtentPlus(org.gvsig.fmap.geom.Geometry geom, float[] distances,
206
                        ViewPort viewPort, int dpi) {
207
                throw new Error("Not yet implemented!");
208

    
209
        }
210

    
211
        public ISymbol getSymbolForSelection() {
212
                return this; // a text is not selectable
213
        }
214

    
215
        public int getSymbolType() {
216
                //This method returned the deleted type Geometry.TYPES.TEXT. I suppose that now
217
            //it has to retrun geometry
218
            return Geometry.TYPES.GEOMETRY;
219
        }
220

    
221
        public boolean isSuitableFor(Geometry geom) {
222
                return true;
223
        }
224

    
225
        public String getClassName() {
226
                return getClass().getName();
227
        }
228

    
229
        public void print(Graphics2D g, AffineTransform at, org.gvsig.fmap.geom.Geometry geom, PrintAttributes properties){
230
                float originalSize = getFont().getSize2D();
231
                float size=originalSize;
232
                // scale it to size
233
                int pq = properties.getPrintQuality();
234
                if (pq == PrintAttributes.PRINT_QUALITY_NORMAL){
235
                        size *= (double) 300/72;
236
                }else if (pq == PrintAttributes.PRINT_QUALITY_HIGH){
237
                        size *= (double) 600/72;
238
                }else if (pq == PrintAttributes.PRINT_QUALITY_DRAFT){
239
                        // d *= 72/72; // (which is the same than doing nothing)
240
                }
241
                setFont(getFont().deriveFont(size));
242
                draw(g,at,geom, null, null);
243
                setFont(getFont().deriveFont(originalSize));
244
        }
245

    
246
        public String getText() {
247
            if (text == null) {
248
                return "";
249
            } else {
250
                return text;
251
            }
252
        }
253

    
254
        public Font getFont() {
255
                return font;
256
        }
257

    
258
        public Color getTextColor() {
259
                return textColor;
260
        }
261

    
262
        public void setText(String text) {
263
                this.text = text;
264
        }
265

    
266
        public void setFont(Font font) {
267
                if (font == null) {
268
                        LOG.warn("font <-- null");
269

    
270
                        return;
271
                }
272
                this.font = font;
273
        }
274

    
275
        public void setTextColor(Color color) {
276
                this.textColor = color;
277
        }
278
        
279
        public Color getColor() {
280
                return getTextColor();
281
        }
282
        
283
        public void setColor(Color color) {
284
                setTextColor(color);
285
        }
286

    
287
        public void setFontSize(double size) {
288
                Font newFont = new Font(font.getName(), font.getStyle(), (int) size);
289
//                if (newFont == null) {
290
//                        logger.warn(
291
//                                        "Font(" + font.getName() + ", "
292
//                                        + font.getStyle() + ", " + (int) size + " --> null");
293
//                } else {
294
                        this.font = newFont;
295
//                }
296
        }
297

    
298
        public double getRotation() {
299
                return rotation;
300
        }
301

    
302
        /**
303
         * Defines the angle of rotation for the text that composes the symbol
304
         *
305
         * @param rotation
306
         */
307
        public void setRotation(double rotation) {
308
                this.rotation = rotation;
309
        }
310

    
311
        /**
312
         * Returns an Geometry which represents a rectangle containing the text in
313
         * <b>screen</b> units.
314
         */
315
        public Geometry getTextWrappingShape(org.gvsig.fmap.geom.primitive.Point p) {
316
                Font font = getFont();
317
                GlyphVector gv = font.createGlyphVector(frc, getText());
318

    
319
                Shape shape = gv.getOutline((float) p.getX(), (float) p.getY());
320
                Geometry myFShape;
321
                try {
322
                        myFShape = geomManager.createSurface(new GeneralPathX(shape
323
                                        .getBounds2D().getPathIterator(null)), SUBTYPES.GEOM2D);
324
                        myFShape.transform(AffineTransform.getTranslateInstance(p.getX(), p.getY()));
325

    
326
                        if (rotation != 0) {
327
                                myFShape.transform(AffineTransform.getRotateInstance(rotation));
328
                        }
329
                        return myFShape;
330
                } catch (CreateGeometryException e) {
331
                        LOG.error("Error creating a surface", e);
332
                        e.printStackTrace();
333
                }
334
                return null;
335
        }
336

    
337
        public Rectangle getBounds() {
338
//                FontMetrics fm = g.getFontMetrics();
339
//                Rectangle2D rect = fm.getStringBounds("graphics", g);
340

    
341
                Rectangle bounds = null;
342
                try {
343
                        bounds = getTextWrappingShape(geomManager.createPoint(0,0, SUBTYPES.GEOM2D)).getShape().getBounds();
344
                } catch (CreateGeometryException e) {
345
                        LOG.error("Error creating a point", e);
346
                }
347
                return bounds;
348
        }
349

    
350
        public void setCartographicSize(double cartographicSize, Geometry geom) {
351
                setFontSize(cartographicSize);
352
        }
353

    
354
        public double toCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
355
                double oldSize = getFont().getSize();
356
                setCartographicSize(getCartographicSize(
357
                                                                viewPort,
358
                                                                dpi,
359
                                                                geom),
360
                                                        geom);
361
                return oldSize;
362
        }
363

    
364
        public double getCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
365
                return CartographicSupportToolkit.
366
                                        getCartographicLength(this,
367
                                                                                  getFont().getSize(),
368
                                                                                  viewPort,
369
                                                                                  dpi);
370
        }
371
        public boolean isAutoresizeEnabled() {
372
                return autoresize;
373
        }
374

    
375
        public void setAutoresizeEnabled(boolean autoresizeFlag) {
376
                this.autoresize = autoresizeFlag;
377
        }
378

    
379
        
380
        public Object clone() throws CloneNotSupportedException {
381
                SimpleTextSymbol copy = (SimpleTextSymbol) super.clone();
382

    
383
                return copy;
384
        }
385

    
386
        public void loadFromState(PersistentState state)
387
        
388
                        throws PersistenceException {
389
                // Set parent fill symbol properties
390
                super.loadFromState(state);
391

    
392
                // Set own properties
393
                setAutoresizeEnabled(state.getBoolean(FIELD_AUTO_RESIZE));
394
                setFont((Font) state.get(FIELD_FONT));
395
                setRotation(state.getDouble(FIELD_ROTATION));
396
                setText(state.getString(FIELD_TEXT));
397
                setTextColor((Color) state.get(FIELD_TEXT_COLOR));
398
                // halo
399
                this.setDrawWithHalo(state.getBoolean(FIELD_HAS_HALO));
400
                this.setHaloColor((Color) state.get(FIELD_HALO_COLOR));
401
                this.setHaloWidth(state.getFloat(FIELD_HALO_WIDTH));
402
        }
403

    
404
        public void saveToState(PersistentState state) throws PersistenceException {
405
                // Save parent fill symbol properties
406
                super.saveToState(state);
407

    
408
                // Save own properties
409
                state.set(FIELD_AUTO_RESIZE, isAutoresizeEnabled());
410
                state.set(FIELD_FONT, getFont());
411
                state.set(FIELD_ROTATION, getRotation());
412
                state.set(FIELD_TEXT, getText());
413
                state.set(FIELD_TEXT_COLOR, getTextColor());
414
                // halo
415
                state.set(FIELD_HAS_HALO, this.isDrawWithHalo());
416
                state.set(FIELD_HALO_COLOR, this.getHaloColor());
417
                state.set(FIELD_HALO_WIDTH, this.getHaloWidth());
418
        }
419

    
420
        public static class RegisterPersistence implements Callable {
421

    
422
                public Object call() throws Exception {
423
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
424
                        if( manager.getDefinition(SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME)==null ) {
425
                                DynStruct definition = manager.addDefinition(
426
                                                SimpleTextSymbol.class,
427
                                                SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME,
428
                                                SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME+" Persistence definition",
429
                                                null, 
430
                                                null
431
                                );
432
                                // Extend the Symbol base definition
433
                                definition.extend(manager.getDefinition(SYMBOL_PERSISTENCE_DEFINITION_NAME));
434

    
435
                                // Auto resize?
436
                                definition.addDynFieldBoolean(FIELD_AUTO_RESIZE)
437
                                        .setMandatory(true);
438
                                
439
                                // Font
440
                                definition.addDynFieldObject(FIELD_FONT)
441
                                        .setClassOfValue(Font.class)
442
                                        .setMandatory(true);
443
                                
444
                                // Rotation
445
                                definition.addDynFieldDouble(FIELD_ROTATION)
446
                                        .setMandatory(true);
447
                                
448
                                // Text
449
                                definition.addDynFieldString(FIELD_TEXT);
450
                                
451
                                // Text color
452
                                definition.addDynFieldObject(FIELD_TEXT_COLOR)
453
                                        .setClassOfValue(Color.class)
454
                                        .setMandatory(true);
455
                                
456
                                // halo
457
                                definition.addDynFieldBoolean(FIELD_HAS_HALO).setMandatory(true);
458
                                definition.addDynFieldObject(FIELD_HALO_COLOR).setClassOfValue(
459
                                                Color.class).setMandatory(true);
460
                                definition.addDynFieldFloat(FIELD_HALO_WIDTH).setMandatory(true);
461
                        }
462
                        return Boolean.TRUE;
463
                }
464
                
465
        }
466

    
467
        public static class RegisterSymbol implements Callable {
468

    
469
                public Object call() throws Exception {
470
                        int[] shapeTypes;
471
                        SymbolManager manager = MapContextLocator.getSymbolManager();
472
                
473
                        //This method registered the deleted type Geometry.TYPES.TEXT. I suppose that now
474
                //it has to register geometry
475
                shapeTypes = new int[] { Geometry.TYPES.GEOMETRY };
476
                manager.registerSymbol(ITextSymbol.SYMBOL_NAME,
477
                    shapeTypes,
478
                    SimpleTextSymbol.class);
479

    
480
                        return Boolean.TRUE;
481
                }
482
                
483
        }
484

    
485
        
486
        public Color getHaloColor() {
487
                return haloColor;
488
        }
489

    
490
        public void setHaloColor(Color co) {
491
                if (co != null) {
492
                        this.haloColor = co;
493
                }
494
        }
495

    
496
        public float getHaloWidth() {
497
                return haloWidth;
498
        }
499

    
500
        public void setHaloWidth(float haloWidth) {
501
                this.haloWidth = haloWidth;
502
                this.haloStroke = new BasicStroke(
503
                                2*haloWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
504
        }
505

    
506
        public boolean isDrawWithHalo() {
507
                return drawWithHalo;
508
        }
509

    
510
        public void setDrawWithHalo(boolean drawWithHalo) {
511
                this.drawWithHalo = drawWithHalo;
512
        }
513

    
514
        
515
}