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

History | View | Annotate | Download (16.2 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
import org.gvsig.fmap.dal.feature.Feature;
39
import org.gvsig.fmap.geom.Geometry;
40
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
41
import org.gvsig.fmap.geom.GeometryLocator;
42
import org.gvsig.fmap.geom.GeometryManager;
43
import org.gvsig.fmap.geom.exception.CreateGeometryException;
44
import org.gvsig.fmap.geom.primitive.GeneralPathX;
45
import org.gvsig.fmap.geom.primitive.Point;
46
import org.gvsig.fmap.mapcontext.MapContextLocator;
47
import org.gvsig.fmap.mapcontext.ViewPort;
48
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
49
import org.gvsig.fmap.mapcontext.rendering.symbols.ITextSymbol;
50
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
51
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolPreferences;
52
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.AbstractSymbol;
53
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.CartographicSupportToolkit;
54
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.text.ISimpleTextSymbol;
55
import org.gvsig.tools.ToolsLocator;
56
import org.gvsig.tools.dynobject.DynStruct;
57
import org.gvsig.tools.persistence.PersistenceManager;
58
import org.gvsig.tools.persistence.PersistentState;
59
import org.gvsig.tools.persistence.exception.PersistenceException;
60
import org.gvsig.tools.task.Cancellable;
61
import org.gvsig.tools.util.Callable;
62
import org.slf4j.Logger;
63
import org.slf4j.LoggerFactory;
64

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

    
75
        public static final String SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME = "SimpleTextSymbol";
76

    
77
        private static final String FIELD_TEXT = "text";
78
        private static final String FIELD_FONT = "font";
79
        private static final String FIELD_TEXT_COLOR = "textColor";
80
        private static final String FIELD_ROTATION = "rotation";
81
        private static final String FIELD_AUTO_RESIZE = "autoResize";
82

    
83
        private static final String FIELD_HAS_HALO = "hasHalo";
84
        private static final String FIELD_HALO_COLOR = "haloColor";
85
        private static final String FIELD_HALO_WIDTH = "haloWidth";
86

    
87
        /*
88
         * This value must be get using getText() if the 'null'
89
         * value has to prevented
90
         */
91
        private String text = "";
92
        private Font font;
93

    
94
        private Color haloColor = Color.WHITE;
95
        private float haloWidth = 3;
96
        private boolean drawWithHalo = false;
97
        private BasicStroke haloStroke = new BasicStroke(
98
                        6, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
99

    
100
        private Color textColor;
101
        private double rotation;
102
        private final FontRenderContext frc = new FontRenderContext(
103
                        new AffineTransform(), false, true);
104
        private boolean autoresize;
105

    
106
        public SimpleTextSymbol() {
107
                super();
108
                SymbolPreferences preferences =
109
                                MapContextLocator.getSymbolManager().getSymbolPreferences();
110
                font = preferences.getDefaultSymbolFont();
111
                textColor = preferences.getDefaultSymbolColor();
112
        }
113

    
114
    @Override
115
    public void draw(Graphics2D g, AffineTransform affineTransform, Geometry geom, Feature f, Cancellable cancel, Rectangle r) {
116
        if (!isShapeVisible()) {
117
            return;
118
        }
119

    
120
        Point point;
121
        if (r != null) {
122
            point = this.getSamplePoint(r);
123
        } else {
124
            try {
125
                point = geom.centroid();
126
            } catch (Exception ex) {
127
                throw new RuntimeException("Can't get centroid of geometry.", ex);
128
            }
129
        }
130
        Font theFont = this.getFont();
131
        if (autoresize && r != null) {
132
            int sz = theFont.getSize();
133
            if (sz == 0) {
134
                sz = 1;
135
                theFont = new Font(theFont.getName(), theFont.getStyle(), sz);
136
            }
137
            g.setFont(theFont);
138
            FontMetrics fm = g.getFontMetrics();
139
            Rectangle2D rect = fm.getStringBounds(getText(), g);
140
            double width = rect.getWidth();
141
            double height = rect.getHeight();
142
            double rWidth = r.getWidth();
143
            double rHeight = r.getHeight();
144
            double ratioText = width / height;
145
            double ratioRect = rWidth / rHeight;
146

    
147
            if (ratioText > ratioRect) {
148
                sz = (int) (sz * (rWidth / width));
149
            } else {
150
                sz = (int) (sz * (rHeight / height));
151
            }
152
            theFont = new Font(theFont.getName(), theFont.getStyle(), sz);
153
        } else {
154
            if(!isInPixels()){
155
                theFont = new Font(theFont.getName(), theFont.getStyle(), (int) toCartographicUnits(theFont.getSize()));
156
            }
157
        }
158

    
159
        point.transform(affineTransform);
160

    
161
        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
162
        g.translate(point.getX(), point.getY());
163

    
164
        g.rotate(rotation);
165

    
166
        if (isDrawWithHalo()) {
167
            char[] charText = new char[getText().length()];
168
            getText().getChars(0, charText.length, charText, 0);
169
            GlyphVector glyph = theFont.layoutGlyphVector(frc, charText, 0, charText.length, Font.LAYOUT_NO_LIMIT_CONTEXT);
170
            g.setColor(getHaloColor());
171
            g.setStroke(haloStroke);
172
            g.draw(glyph.getOutline());
173
        }
174
        // Rectangle2D bounds = getBounds();
175
//                 getBounds devuelve el bounds de un texto dibujado de manera que
176
        // la linea base de la primera letra est? en el punto (0,0).
177
        // Por eso, si queremos alinear el texto de manera que la parte superior
178
        // izquierda de la primera letra est? en (0,0) debemos moverlo seg?n
179
        // el valor de la ordenada, en lugar de seg?n su altura.
180
        g.setColor(textColor);
181
        g.setFont(theFont);
182

    
183
        g.drawString(getText(), 0, 0);//(int) bounds.getHeight());
184
        g.rotate(-rotation);
185
        g.translate(-point.getX(), -point.getY());
186
    }
187

    
188
//        @Override
189
//        public int getOnePointRgb() {
190
//                return textColor.getRGB();
191
//        }
192
//
193
//        @Override
194
//        public void getPixExtentPlus(org.gvsig.fmap.geom.Geometry geom, float[] distances,
195
//                        ViewPort viewPort, int dpi) {
196
//                throw new Error("Not yet implemented!");
197
//
198
//        }
199

    
200
        @Override
201
        public ISymbol getSymbolForSelection() {
202
                return this; // a text is not selectable
203
        }
204

    
205
        @Override
206
        public int getSymbolType() {
207
                //This method returned the deleted type Geometry.TYPES.TEXT. I suppose that now
208
            //it has to retrun geometry
209
            return Geometry.TYPES.GEOMETRY;
210
        }
211

    
212
        @Override
213
        public boolean isSuitableFor(Geometry geom) {
214
                return true;
215
        }
216

    
217
        public String getClassName() {
218
                return getClass().getName();
219
        }
220

    
221
        @Override
222
        public String getText() {
223
            if (text == null) {
224
                return "";
225
            } else {
226
                return text;
227
            }
228
        }
229

    
230
        @Override
231
        public Font getFont() {
232
                return font;
233
        }
234

    
235
        @Override
236
        public Color getTextColor() {
237
                return textColor;
238
        }
239

    
240
        @Override
241
        public void setText(String text) {
242
                this.text = text;
243
        }
244

    
245
        @Override
246
        public void setFont(Font font) {
247
                if (font == null) {
248
                        LOG.warn("font <-- null");
249

    
250
                        return;
251
                }
252
                this.font = font;
253
        }
254

    
255
        @Override
256
        public void setTextColor(Color color) {
257
                this.textColor = color;
258
        }
259

    
260
        @Override
261
        public Color getColor() {
262
                return getTextColor();
263
        }
264

    
265
        @Override
266
        public void setColor(Color color) {
267
                setTextColor(color);
268
        }
269

    
270
        @Override
271
        public void setFontSize(double size) {
272
                Font newFont = new Font(font.getName(), font.getStyle(), (int) size);
273
                this.font = newFont;
274
        }
275

    
276
        @Override
277
        public double getRotation() {
278
                return rotation;
279
        }
280

    
281
        /**
282
         * Defines the angle of rotation for the text that composes the symbol
283
         *
284
         * @param rotation
285
         */
286
        @Override
287
        public void setRotation(double rotation) {
288
                this.rotation = rotation;
289
        }
290

    
291
        /**
292
         * Returns an Geometry which represents a rectangle containing the text in
293
         * <b>screen</b> units.
294
         */
295
        @Override
296
        public Geometry getTextWrappingShape(org.gvsig.fmap.geom.primitive.Point p) {
297
                GeometryManager geomManager = GeometryLocator.getGeometryManager();
298

    
299
                Font theFont = getFont();
300
                GlyphVector gv = theFont.createGlyphVector(frc, getText());
301

    
302
                Shape shape = gv.getOutline((float) p.getX(), (float) p.getY());
303
                Geometry myFShape;
304
                try {
305
                        myFShape = geomManager.createSurface(new GeneralPathX(shape
306
                                        .getBounds2D().getPathIterator(null)), SUBTYPES.GEOM2D);
307
                        myFShape.transform(AffineTransform.getTranslateInstance(p.getX(), p.getY()));
308

    
309
                        if (rotation != 0) {
310
                                myFShape.transform(AffineTransform.getRotateInstance(rotation));
311
                        }
312
                        return myFShape;
313
                } catch (CreateGeometryException e) {
314
                        LOG.error("Error creating a surface", e);
315
                }
316
                return null;
317
        }
318

    
319
        @Override
320
        public Rectangle getBounds() {
321
            GeometryManager geomManager = GeometryLocator.getGeometryManager();
322

    
323

    
324
                Rectangle bounds = null;
325
                try {
326
                        bounds = getTextWrappingShape(geomManager.createPoint(0,0, SUBTYPES.GEOM2D)).getShape().getBounds();
327
                } catch (CreateGeometryException e) {
328
                        LOG.error("Error creating a point", e);
329
                }
330
                return bounds;
331
        }
332

    
333
        public void setCartographicSize(double cartographicSize, Geometry geom) {
334
                setFontSize(cartographicSize);
335
        }
336

    
337
        public double toCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
338
                double oldSize = getFont().getSize();
339
                setCartographicSize(getCartographicSize(
340
                                                                viewPort,
341
                                                                dpi,
342
                                                                geom),
343
                                                        geom);
344
                return oldSize;
345
        }
346

    
347
        public double getCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
348
                return CartographicSupportToolkit.
349
                                        getCartographicLength(this,
350
                                                                                  getFont().getSize(),
351
                                                                                  viewPort,
352
                                                                                  dpi);
353
        }
354
        @Override
355
        public boolean isAutoresizeEnabled() {
356
                return autoresize;
357
        }
358

    
359
        @Override
360
        public void setAutoresizeEnabled(boolean autoresizeFlag) {
361
                this.autoresize = autoresizeFlag;
362
        }
363

    
364

    
365
        @Override
366
        public Object clone() throws CloneNotSupportedException {
367
                SimpleTextSymbol copy = (SimpleTextSymbol) super.clone();
368

    
369
                return copy;
370
        }
371

    
372
        @Override
373
        public void loadFromState(PersistentState state)
374

    
375
                        throws PersistenceException {
376
                // Set parent fill symbol properties
377
                super.loadFromState(state);
378

    
379
                // Set own properties
380
                setAutoresizeEnabled(state.getBoolean(FIELD_AUTO_RESIZE));
381
                setFont((Font) state.get(FIELD_FONT));
382
                setRotation(state.getDouble(FIELD_ROTATION));
383
                setText(state.getString(FIELD_TEXT));
384
                setTextColor((Color) state.get(FIELD_TEXT_COLOR));
385
                // halo
386
                this.setDrawWithHalo(state.getBoolean(FIELD_HAS_HALO));
387
                this.setHaloColor((Color) state.get(FIELD_HALO_COLOR));
388
                this.setHaloWidth(state.getFloat(FIELD_HALO_WIDTH));
389
        }
390

    
391
        @Override
392
        public void saveToState(PersistentState state) throws PersistenceException {
393
                // Save parent fill symbol properties
394
                super.saveToState(state);
395

    
396
                // Save own properties
397
                state.set(FIELD_AUTO_RESIZE, isAutoresizeEnabled());
398
                state.set(FIELD_FONT, getFont());
399
                state.set(FIELD_ROTATION, getRotation());
400
                state.set(FIELD_TEXT, getText());
401
                state.set(FIELD_TEXT_COLOR, getTextColor());
402
                // halo
403
                state.set(FIELD_HAS_HALO, this.isDrawWithHalo());
404
                state.set(FIELD_HALO_COLOR, this.getHaloColor());
405
                state.set(FIELD_HALO_WIDTH, this.getHaloWidth());
406
        }
407

    
408
        public static class RegisterPersistence implements Callable {
409

    
410
                @Override
411
                public Object call() throws Exception {
412
                        PersistenceManager manager = ToolsLocator.getPersistenceManager();
413
                        if( manager.getDefinition(SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME)==null ) {
414
                                DynStruct definition = manager.addDefinition(
415
                                                SimpleTextSymbol.class,
416
                                                SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME,
417
                                                SIMPLE_TEXT_SYMBOL_PERSISTENCE_DEFINITION_NAME+" Persistence definition",
418
                                                null,
419
                                                null
420
                                );
421
                                // Extend the Symbol base definition
422
                                definition.extend(manager.getDefinition(SYMBOL_PERSISTENCE_DEFINITION_NAME));
423

    
424
                                // Auto resize?
425
                                definition.addDynFieldBoolean(FIELD_AUTO_RESIZE)
426
                                        .setMandatory(true);
427

    
428
                                // Font
429
                                definition.addDynFieldObject(FIELD_FONT)
430
                                        .setClassOfValue(Font.class)
431
                                        .setMandatory(true);
432

    
433
                                // Rotation
434
                                definition.addDynFieldDouble(FIELD_ROTATION)
435
                                        .setMandatory(true);
436

    
437
                                // Text
438
                                definition.addDynFieldString(FIELD_TEXT);
439

    
440
                                // Text color
441
                                definition.addDynFieldObject(FIELD_TEXT_COLOR)
442
                                        .setClassOfValue(Color.class)
443
                                        .setMandatory(true);
444

    
445
                                // halo
446
                                definition.addDynFieldBoolean(FIELD_HAS_HALO).setMandatory(true);
447
                                definition.addDynFieldObject(FIELD_HALO_COLOR).setClassOfValue(
448
                                                Color.class).setMandatory(true);
449
                                definition.addDynFieldFloat(FIELD_HALO_WIDTH).setMandatory(true);
450
                        }
451
                        return Boolean.TRUE;
452
                }
453

    
454
        }
455

    
456
        public static class RegisterSymbol implements Callable {
457

    
458
                @Override
459
                public Object call() throws Exception {
460
                        int[] shapeTypes;
461
                        SymbolManager manager = MapContextLocator.getSymbolManager();
462

    
463
                        //This method registered the deleted type Geometry.TYPES.TEXT. I suppose that now
464
                //it has to register geometry
465
                shapeTypes = new int[] { Geometry.TYPES.GEOMETRY };
466
                manager.registerSymbol(ITextSymbol.SYMBOL_NAME,
467
                    shapeTypes,
468
                    SimpleTextSymbol.class);
469

    
470
                        return Boolean.TRUE;
471
                }
472

    
473
        }
474

    
475

    
476
        @Override
477
        public Color getHaloColor() {
478
                return haloColor;
479
        }
480

    
481
        @Override
482
        public void setHaloColor(Color co) {
483
                if (co != null) {
484
                        this.haloColor = co;
485
                }
486
        }
487

    
488
        @Override
489
        public float getHaloWidth() {
490
                return haloWidth;
491
        }
492

    
493
        @Override
494
        public void setHaloWidth(float haloWidth) {
495
                this.haloWidth = haloWidth;
496
                this.haloStroke = new BasicStroke(
497
                                2*haloWidth, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND);
498
        }
499

    
500
        @Override
501
        public boolean isDrawWithHalo() {
502
                return drawWithHalo;
503
        }
504

    
505
        @Override
506
        public void setDrawWithHalo(boolean drawWithHalo) {
507
                this.drawWithHalo = drawWithHalo;
508
        }
509

    
510
    protected Point getSamplePoint(Rectangle r) {
511
        GeometryManager geomManager = GeometryLocator.getGeometryManager();
512
        try {
513
            return geomManager.createPoint(
514
                    r.getMinX(),
515
                    // Small adjustment to center the fonts
516
                    r.getMinY() + 0.725 * r.getHeight(),
517
                    SUBTYPES.GEOM2D);
518

    
519
        } catch (Exception e) {
520
            throw new RuntimeException("Can't create sample point.", e);
521
        }
522

    
523
    }
524

    
525
}