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 / fill / impl / PictureFillSymbol.java @ 47790

History | View | Annotate | Download (15.4 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.fill.impl;
25

    
26
import java.awt.Color;
27
import java.awt.Dimension;
28
import java.awt.Graphics2D;
29
import java.awt.Paint;
30
import java.awt.Rectangle;
31
import java.awt.RenderingHints;
32
import java.awt.Shape;
33
import java.awt.TexturePaint;
34
import java.awt.geom.AffineTransform;
35
import java.awt.image.BufferedImage;
36
import java.io.IOException;
37
import java.net.URL;
38
import org.gvsig.fmap.dal.feature.Feature;
39
import org.gvsig.fmap.geom.Geometry;
40
import org.gvsig.fmap.mapcontext.MapContextLocator;
41
import org.gvsig.fmap.mapcontext.ViewPort;
42
import org.gvsig.fmap.mapcontext.rendering.symbols.CartographicSupport;
43
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
44
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol_v2;
45
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolDrawingException;
46
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
47
import org.gvsig.fmap.mapcontext.rendering.symbols.styles.IBackgroundFileStyle;
48
import org.gvsig.i18n.Messages;
49
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.fill.IPictureFillSymbol;
50
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.BackgroundFileStyle;
51
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.IMarkerFillPropertiesStyle;
52
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.SimpleMarkerFillPropertiesStyle;
53
import org.gvsig.tools.ToolsLocator;
54
import org.gvsig.tools.dynobject.DynStruct;
55
import org.gvsig.tools.persistence.PersistenceManager;
56
import org.gvsig.tools.persistence.PersistentState;
57
import org.gvsig.tools.persistence.exception.PersistenceException;
58
import org.gvsig.tools.task.Cancellable;
59
import org.gvsig.tools.util.Callable;
60
import org.slf4j.Logger;
61
import org.slf4j.LoggerFactory;
62

    
63
/**
64
 * PictureFillSymbol allows to use an image file suported by gvSIG as a padding
65
 * for the polygons.This image can be modified using methods to scale or rotate it.
66
 * @author jaume dominguez faus - jaume.dominguez@iver.es
67
 */
68
public class PictureFillSymbol extends AbstractFillSymbol implements IPictureFillSymbol {
69
        private static final Logger logger = LoggerFactory.getLogger(PictureFillSymbol.class);
70

    
71
    public static final String PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME =
72
        "PictureFillSymbol";
73
    private static final String SELECTED = "selected";
74
    private static final String SELECTION_SYMBOL = "selectionSym";
75
    private static final String BACKGROUND_IMAGE = "bgImage";
76
    private static final String BACKGROUND_SELECTION_IMAGE = "bgSelImage";
77
    private static final String ANGLE = "angle";
78
    private static final String X_SCALE = "xScale";
79
    private static final String Y_SCALE = "yScale";
80
    private static final String MARKER_FILL_PROPERTIES = "markerFillProperties";
81

    
82
        private double angle = 0;
83
        private double xScale = 1;
84
        private double yScale = 1;
85
        transient private PictureFillSymbol selectionSym;
86
        private boolean selected;
87
        private IMarkerFillPropertiesStyle markerFillProperties = new SimpleMarkerFillPropertiesStyle();
88
        private BackgroundFileStyle bgImage;
89
        private BackgroundFileStyle bgSelImage;
90

    
91
    @Override
92
    public void draw(Graphics2D g, AffineTransform affineTransform, Geometry geom, Feature f, Cancellable cancel, Rectangle r) {
93
                 Color fillColor = getFillColor();
94

    
95
                if(r != null){
96
                    geom = getSampleGeometry(r);
97
                }
98
                 Shape transf_geom = geom.getShape(affineTransform);
99

    
100
                if (fillColor != null) {
101
                        g.setColor(fillColor);
102
                        g.fill(transf_geom);
103
                }
104

    
105
                g.setClip(transf_geom);
106
                BackgroundFileStyle bg = (!selected) ? bgImage : bgSelImage ;
107
                if (bg != null && bg.getBounds() != null){
108
                        int sizeW=(int) ((int)bg.getBounds().getWidth() * xScale);
109
                        int sizeH=(int) ((int)bg.getBounds().getHeight() * yScale);
110
                        Rectangle rProv = new Rectangle();
111
                        rProv.setFrame(0,0,sizeW,sizeH);
112
                        Paint resulPatternFill;
113
                        BufferedImage sample;
114

    
115
                        sample= new BufferedImage(sizeW,sizeH,BufferedImage.TYPE_INT_ARGB);
116
                        Graphics2D gAux = sample.createGraphics();
117

    
118
                        double xSeparation = markerFillProperties.getXSeparation(); // TODO apply CartographicSupport
119
                        double ySeparation = markerFillProperties.getYSeparation(); // TODO apply CartographicSupport
120
                        double xOffset = markerFillProperties.getXOffset();
121
                        double yOffset = markerFillProperties.getYOffset();
122

    
123
                        try {
124
                                bg.drawInsideRectangle(gAux, rProv);
125
                        } catch (SymbolDrawingException e) {
126
                                logger.warn(Messages.getText("label_style_could_not_be_painted"), e);
127
                        }
128

    
129
                        Dimension sz = rProv.getSize();
130
                        sz = new Dimension((int) Math.round(sz.getWidth()+(xSeparation)),
131
                                        (int) Math.round(sz.getHeight() + (ySeparation)));
132
                        rProv.setSize(sz);
133

    
134
                        BufferedImage bi = new BufferedImage(rProv.width, rProv.height, BufferedImage.TYPE_INT_ARGB);
135
                        gAux = bi.createGraphics();
136
                        gAux.drawImage(sample, null, (int) (xSeparation*0.5), (int) (ySeparation*0.5));
137

    
138
                        rProv.x = rProv.x+(int)xOffset;
139
                        rProv.y = rProv.y+(int)yOffset;
140
                        resulPatternFill = new TexturePaint(bi,rProv);
141

    
142
                        g.setColor(null);
143
                        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
144
                                        RenderingHints.VALUE_ANTIALIAS_ON);
145

    
146
                        g.setPaint(resulPatternFill);
147
                }
148

    
149
                if (angle == 0) {
150
                    g.fill(transf_geom);
151
                } else {
152

    
153
            AffineTransform at = new AffineTransform();
154
            at.rotate(angle);
155

    
156
                    Geometry pixels_geom = geom.cloneGeometry();
157
                    pixels_geom.transform(affineTransform);
158
                    pixels_geom.transform(at);
159

    
160
                    g.rotate(-angle);
161
                    g.fill(pixels_geom.getShape());
162
                    g.rotate(angle);
163
                }
164

    
165
                g.setClip(null);
166
                if (getOutline()!=null) {
167
                    getOutline().setCartographicContext(this.getCartographicContext());
168
                    if(getOutline() instanceof ISymbol_v2){
169
                        ((ISymbol_v2)getOutline()).draw(g, affineTransform, geom, null, cancel, null);
170
                    } else {
171
                        getOutline().draw(g, affineTransform, geom, null, cancel);
172
                    }
173
                }
174
        }
175

    
176
        /**
177
         * Constructor method
178
         *
179
         */
180
        public PictureFillSymbol() {
181
                super();
182
        }
183
        /**
184
         * Constructor method
185
         * @param imageURL, URL of the normal image
186
         * @param selImageURL, URL of the image when it is selected in the map
187
         * @throws IOException
188
         */
189

    
190
        public PictureFillSymbol(URL imageURL, URL selImageURL) throws IOException {
191
                setImage(imageURL);
192
                if (selImageURL!=null)
193
                        setSelImage(selImageURL);
194
                else setSelImage(imageURL);
195
        }
196

    
197

    
198
        /**
199
         * Sets the URL for the image to be used as a picture fill symbol (when it is selected in the map)
200
         * @param imageFile, File
201
         * @throws IOException
202
         */
203
        @Override
204
        public final void setSelImage(URL selImageUrl) throws IOException{
205

    
206
                bgSelImage= BackgroundFileStyle.createStyleByURL(selImageUrl);
207
        }
208

    
209

    
210

    
211
        /**
212
         * Defines the URL from where the picture to fill the polygon is taken.
213
         * @param imageFile
214
         * @throws IOException
215
         */
216
        @Override
217
        public final void setImage(URL imageUrl) throws IOException{
218
                bgImage = BackgroundFileStyle.createStyleByURL(imageUrl);
219
        }
220

    
221
        @Override
222
        public ISymbol getSymbolForSelection(Color selectionColor) {
223
                if (selectionSym == null) {
224
                        selectionSym = (PictureFillSymbol) cloneForSelection(selectionColor);
225
                        selectionSym.selected=true;
226
                        selectionSym.selectionSym = selectionSym; // avoid too much lazy creations
227
                }else {
228
                    selectionSym.setFillColor(selectionColor);
229
                }
230
                if (selectionSym instanceof CartographicSupport) {
231
                    ((CartographicSupport) selectionSym).setUnit(this.getUnit());
232
                }
233
                return selectionSym;
234

    
235
        }
236

    
237
        @Override
238
        public int getSymbolType() {
239
                return Geometry.TYPES.SURFACE;
240
        }
241

    
242
        public String getClassName() {
243
                return getClass().getName();
244
        }
245

    
246
        /**
247
         * Returns the IMarkerFillProperties that allows this class to treat the picture as
248
         * a marker in order to scale it, rotate it and so on.
249
         * @return markerFillProperties,IMarkerFillPropertiesStyle
250
         */
251
        @Override
252
        public IMarkerFillPropertiesStyle getMarkerFillProperties() {
253
                return markerFillProperties;
254
        }
255

    
256
        /**
257
         * Sets the MarkerFillProperties in order to allow the user to modify the picture as
258
         * a marker (it is possible to scale it, rotate it and so on)
259
         * @param prop
260
         */
261
        @Override
262
        public void setMarkerFillProperties(IMarkerFillPropertiesStyle prop) {
263
                this.markerFillProperties = prop;
264
        }
265
        /**
266
         * Defines the angle for the rotation of the image when it is added to create the
267
         * padding
268
         *
269
         * @return angle
270
         */
271
        @Override
272
        public double getAngle() {
273
                return angle;
274
        }
275
        /**
276
         * Sets the angle for the rotation of the image when it is added to create the padding
277
         * @param angle
278
         */
279
        @Override
280
        public void setAngle(double angle) {
281
                this.angle = angle;
282
        }
283

    
284
        /**
285
         * Defines the scale for the x axis of the image when it is added to create the
286
         * padding
287
         * @return xScale
288
         */
289
        @Override
290
        public double getXScale() {
291
                return xScale;
292
        }
293
        /**
294
         * Returns the scale for the x axis of the image when it is added to create the
295
         * padding
296
         * @param xScale
297
         */
298
        @Override
299
        public void setXScale(double xScale) {
300
                this.xScale = xScale;
301
        }
302
        /**
303
         * Defines the scale for the y axis of the image when it is added to create the
304
         * padding
305
         * @return yScale
306
         */
307
        @Override
308
        public double getYScale() {
309
                return yScale;
310
        }
311
        /**
312
         * Returns the scale for the y axis of the image when it is added to create the
313
         * padding
314
         * @param yScale
315
         */
316
        @Override
317
        public void setYScale(double yScale) {
318
                this.yScale = yScale;
319
        }
320

    
321
        /**
322
         * Returns the URL of the image that is used to create the padding to fill the
323
         * polygon
324
         * @return imagePath
325
         */
326
        @Override
327
        public URL getSource() {
328
                if (bgImage != null){
329
                        return bgImage.getSource();
330
                }
331
                return null;
332
        }
333

    
334
        /**
335
         * Returns the URL of the image used when the polygon is selected
336
         * @return
337
         */
338
        @Override
339
        public URL getSelectedSource(){
340
                if( bgSelImage != null){
341
                        return bgSelImage.getSource();
342
                }
343
                return null;
344
        }
345

    
346

    
347

    
348
        @Override
349
        public double toCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
350
                return super.toCartographicSize(viewPort, dpi, geom);
351
                // this symbol cannot apply any cartographic transfomation to its filling
352
        }
353

    
354
    @Override
355
    public Object clone() throws CloneNotSupportedException {
356
            PictureFillSymbol copy = (PictureFillSymbol) super.clone();
357

    
358
        // clone selection
359
            if (selectionSym != null) {
360
                    //Parche por lo que hace el m?todo getSymbolForSelection
361
                    if (this == selectionSym){
362
                            copy.selectionSym = copy;
363
                    } else {
364
                            copy.selectionSym = (PictureFillSymbol) selectionSym.clone();
365
                    }
366
            }
367

    
368
        // clone brackground image
369
        if (bgImage != null) {
370
            copy.bgImage = (BackgroundFileStyle) bgImage.clone();
371
        }
372

    
373
        // clone selection brackground image
374
        if (bgSelImage != null) {
375
            copy.bgSelImage = (BackgroundFileStyle) bgSelImage.clone();
376
        }
377

    
378
        // FIXME: clone properties
379

    
380
        return copy;
381
    }
382

    
383
    @Override
384
    public void loadFromState(PersistentState state) throws PersistenceException {
385
        // Set parent style properties
386
        super.loadFromState(state);
387

    
388
        this.selected = (Boolean) state.get(SELECTED);
389
        this.selectionSym = (PictureFillSymbol) state.get(SELECTION_SYMBOL);
390
        this.bgImage = (BackgroundFileStyle) state.get(BACKGROUND_IMAGE);
391
        this.bgSelImage =
392
            (BackgroundFileStyle) state.get(BACKGROUND_SELECTION_IMAGE);
393

    
394

    
395
        this.angle = (Double) state.get(ANGLE);
396
        this.xScale = (Double) state.get(X_SCALE);
397
        this.yScale = (Double) state.get(Y_SCALE);
398
        this.markerFillProperties = (IMarkerFillPropertiesStyle) state.get(MARKER_FILL_PROPERTIES);
399

    
400

    
401
    }
402

    
403
    @Override
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(SELECTED, this.selected);
410
        state.set(SELECTION_SYMBOL, this.getSymbolForSelection());
411
        state.set(BACKGROUND_IMAGE, this.bgImage);
412
        state.set(BACKGROUND_SELECTION_IMAGE, this.bgSelImage);
413
        state.set(ANGLE, this.angle);
414
        state.set(X_SCALE, this.xScale);
415
        state.set(Y_SCALE, this.yScale);
416
        state.set(MARKER_FILL_PROPERTIES, this.markerFillProperties);
417
    }
418

    
419
    public static class RegisterPersistence implements Callable {
420

    
421
        @Override
422
        public Object call() throws Exception {
423
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
424
            if (manager.getDefinition(PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME) == null) {
425
                DynStruct definition =
426
                    manager.addDefinition(PictureFillSymbol.class,
427
                                    PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME,
428
                                    PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME
429
                            + " Persistence definition",
430
                        null,
431
                        null);
432

    
433
                // Extend the Style base definition
434
                definition.extend(manager.getDefinition(FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME));
435

    
436
                definition.addDynFieldBoolean(SELECTED).setMandatory(false);
437
                definition.addDynFieldObject(SELECTION_SYMBOL)
438
                    .setClassOfValue(PictureFillSymbol.class).setMandatory(false);
439
                definition.addDynFieldObject(BACKGROUND_IMAGE)
440
                    .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
441
                definition.addDynFieldObject(BACKGROUND_SELECTION_IMAGE)
442
                    .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
443
                definition.addDynFieldDouble(ANGLE).setMandatory(true);
444
                definition.addDynFieldDouble(X_SCALE).setMandatory(true);
445
                definition.addDynFieldDouble(Y_SCALE).setMandatory(true);
446
                definition.addDynFieldObject(MARKER_FILL_PROPERTIES)
447
                .setClassOfValue(IMarkerFillPropertiesStyle.class).setMandatory(true);
448
            }
449
            return Boolean.TRUE;
450
        }
451
    }
452

    
453
        public static class RegisterSymbol implements Callable {
454

    
455
                @Override
456
                public Object call() throws Exception {
457
                SymbolManager manager = MapContextLocator.getSymbolManager();
458

    
459
                manager.registerSymbol(PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME,
460
                    PictureFillSymbol.class);
461

    
462
                        return Boolean.TRUE;
463
                }
464

    
465
        }
466

    
467
        @Override
468
        public IBackgroundFileStyle getBackgroundFileStyle() {
469
                return this.bgImage;
470
        }
471

    
472
        @Override
473
        public IBackgroundFileStyle getSelectedBackgroundFileStyle() {
474
                return this.bgSelImage;
475
        }
476

    
477
}