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

History | View | Annotate | Download (15.8 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

    
39
import org.gvsig.compat.print.PrintAttributes;
40
import org.gvsig.fmap.dal.feature.Feature;
41
import org.gvsig.fmap.geom.Geometry;
42
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
43
import org.gvsig.fmap.geom.Geometry.TYPES;
44
import org.gvsig.fmap.geom.GeometryLocator;
45
import org.gvsig.fmap.geom.GeometryManager;
46
import org.gvsig.fmap.geom.exception.CreateGeometryException;
47
import org.gvsig.fmap.geom.primitive.GeneralPathX;
48
import org.gvsig.fmap.geom.primitive.Surface;
49
import org.gvsig.fmap.mapcontext.MapContext;
50
import org.gvsig.fmap.mapcontext.MapContextLocator;
51
import org.gvsig.fmap.mapcontext.ViewPort;
52
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
53
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolDrawingException;
54
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
55
import org.gvsig.fmap.mapcontext.rendering.symbols.styles.IBackgroundFileStyle;
56
import org.gvsig.i18n.Messages;
57
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.fill.IPictureFillSymbol;
58
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.BackgroundFileStyle;
59
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.IMarkerFillPropertiesStyle;
60
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.SimpleMarkerFillPropertiesStyle;
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
import org.slf4j.Logger;
69
import org.slf4j.LoggerFactory;
70

    
71
/**
72
 * PictureFillSymbol allows to use an image file suported by gvSIG as a padding
73
 * for the polygons.This image can be modified using methods to scale or rotate it.
74
 * @author jaume dominguez faus - jaume.dominguez@iver.es
75
 */
76
public class PictureFillSymbol extends AbstractFillSymbol implements IPictureFillSymbol {
77
        private static final Logger logger = LoggerFactory.getLogger(PictureFillSymbol.class);
78

    
79
    public static final String PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME =
80
        "PictureFillSymbol";
81
    private static final String SELECTED = "selected";
82
    private static final String SELECTION_SYMBOL = "selectionSym";
83
    private static final String BACKGROUND_IMAGE = "bgImage";
84
    private static final String BACKGROUND_SELECTION_IMAGE = "bgSelImage";
85
    private static final String ANGLE = "angle";
86
    private static final String X_SCALE = "xScale";
87
    private static final String Y_SCALE = "yScale";
88
    private static final String MARKER_FILL_PROPERTIES = "markerFillProperties";
89

    
90
        private double angle = 0;
91
        private double xScale = 1;
92
        private double yScale = 1;
93
        transient private PictureFillSymbol selectionSym;
94
        private boolean selected;
95
        private IMarkerFillPropertiesStyle markerFillProperties = new SimpleMarkerFillPropertiesStyle();
96
        private BackgroundFileStyle bgImage;
97
        private BackgroundFileStyle bgSelImage;
98
        private PrintAttributes properties;
99
        private GeometryManager geometryManager = GeometryLocator.getGeometryManager();
100

    
101
        private void draw(Graphics2D g, AffineTransform affineTransform, Geometry geom, Cancellable cancel) {
102
                 Color fillColor = getFillColor();
103

    
104
                 Shape transf_geom = geom.getShape(affineTransform);
105

    
106
                if (fillColor != null) {
107
                        g.setColor(fillColor);
108
                        g.fill(transf_geom);
109
                }
110

    
111
                g.setClip(transf_geom);
112
                BackgroundFileStyle bg = (!selected) ? bgImage : bgSelImage ;
113
                if (bg != null && bg.getBounds() != null){
114
                        int sizeW=(int) ((int)bg.getBounds().getWidth() * xScale);
115
                        int sizeH=(int) ((int)bg.getBounds().getHeight() * yScale);
116
                        Rectangle rProv = new Rectangle();
117
                        rProv.setFrame(0,0,sizeW,sizeH);
118
                        Paint resulPatternFill = null;
119
                        BufferedImage sample= null;
120

    
121
                        sample= new BufferedImage(sizeW,sizeH,BufferedImage.TYPE_INT_ARGB);
122
                        Graphics2D gAux = sample.createGraphics();
123

    
124
                        double xSeparation = markerFillProperties.getXSeparation(); // TODO apply CartographicSupport
125
                        double ySeparation = markerFillProperties.getYSeparation(); // TODO apply CartographicSupport
126
                        double xOffset = markerFillProperties.getXOffset();
127
                        double yOffset = markerFillProperties.getYOffset();
128

    
129
                        try {
130
                                bg.drawInsideRectangle(gAux, rProv);
131
                        } catch (SymbolDrawingException e) {
132
                                logger.warn(Messages.getText("label_style_could_not_be_painted"), e);
133
                        }
134

    
135
                        Dimension sz = rProv.getSize();
136
                        sz = new Dimension((int) Math.round(sz.getWidth()+(xSeparation)),
137
                                        (int) Math.round(sz.getHeight() + (ySeparation)));
138
                        rProv.setSize(sz);
139

    
140
                        BufferedImage bi = new BufferedImage(rProv.width, rProv.height, BufferedImage.TYPE_INT_ARGB);
141
                        gAux = bi.createGraphics();
142
                        gAux.drawImage(sample, null, (int) (xSeparation*0.5), (int) (ySeparation*0.5));
143

    
144
                        rProv.x = rProv.x+(int)xOffset;
145
                        rProv.y = rProv.y+(int)yOffset;
146
                        resulPatternFill = new TexturePaint(bi,rProv);
147

    
148
                        g.setColor(null);
149
                        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
150
                                        RenderingHints.VALUE_ANTIALIAS_ON);
151

    
152
                        g.setPaint(resulPatternFill);
153
                }
154

    
155
                if (angle == 0) {
156
                    g.fill(transf_geom);
157
                } else {
158

    
159
            AffineTransform at = new AffineTransform();
160
            at.rotate(angle);
161

    
162
                    Geometry pixels_geom = geom.cloneGeometry();
163
                    pixels_geom.transform(affineTransform);
164
                    pixels_geom.transform(at);
165

    
166
                    g.rotate(-angle);
167
                    g.fill(pixels_geom.getShape());
168
                    g.rotate(angle);
169
                }
170

    
171
                g.setClip(null);
172
                if (getOutline()!=null) {
173
                        getOutline().draw(g, affineTransform, geom, null, cancel);
174
                }
175
        }
176

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

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

    
198

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

    
206
                bgSelImage= BackgroundFileStyle.createStyleByURL(selImageUrl);
207
//                selImagePath = selImageUrl.toString();
208
        }
209

    
210

    
211

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

    
221

    
222

    
223
        public void drawInsideRectangle(Graphics2D g,
224
                        AffineTransform scaleInstance, Rectangle r, PrintAttributes properties) throws SymbolDrawingException {
225
                Surface surf;
226
                try {
227
                        surf = geometryManager.createSurface(new GeneralPathX(r.getPathIterator(new AffineTransform())), SUBTYPES.GEOM2D);
228
                        if (properties==null){
229
                                //                        draw(g, null, new FPolygon2D(new GeneralPathX(r)), null);
230
                                draw(g, null, surf, null);
231
                        } else {
232
                                print(g, new AffineTransform(), surf, properties);
233
                        }
234
                } catch (CreateGeometryException e) {
235
                        throw new SymbolDrawingException(TYPES.POINT);
236
                }
237
        }
238

    
239
        public ISymbol getSymbolForSelection() {
240
                if (selectionSym == null) {
241
                        selectionSym = (PictureFillSymbol) cloneForSelection();
242
                        selectionSym.selected=true;
243
                        selectionSym.selectionSym = selectionSym; // avoid too much lazy creations
244
                }else {
245
                    selectionSym.setFillColor(MapContext.getSelectionColor());
246
                }
247
                return selectionSym;
248

    
249
        }
250

    
251
        public int getSymbolType() {
252
                return Geometry.TYPES.SURFACE;
253
        }
254

    
255
        public String getClassName() {
256
                return getClass().getName();
257
        }
258

    
259
        public void print(Graphics2D g, AffineTransform at, Geometry geom,
260
                        PrintAttributes properties) {
261
                this.properties=properties;
262
        draw(g, at, geom, null);
263
        this.properties=null;
264
        }
265
        /**
266
         * Returns the IMarkerFillProperties that allows this class to treat the picture as
267
         * a marker in order to scale it, rotate it and so on.
268
         * @return markerFillProperties,IMarkerFillPropertiesStyle
269
         */
270
        public IMarkerFillPropertiesStyle getMarkerFillProperties() {
271
                return markerFillProperties;
272
        }
273

    
274
        /**
275
         * Sets the MarkerFillProperties in order to allow the user to modify the picture as
276
         * a marker (it is possible to scale it, rotate it and so on)
277
         * @param prop
278
         */
279
        public void setMarkerFillProperties(IMarkerFillPropertiesStyle prop) {
280
                this.markerFillProperties = prop;
281
        }
282
        /**
283
         * Defines the angle for the rotation of the image when it is added to create the
284
         * padding
285
         *
286
         * @return angle
287
         */
288
        public double getAngle() {
289
                return angle;
290
        }
291
        /**
292
         * Sets the angle for the rotation of the image when it is added to create the padding
293
         * @param angle
294
         */
295
        public void setAngle(double angle) {
296
                this.angle = angle;
297
        }
298

    
299
        /**
300
         * Defines the scale for the x axis of the image when it is added to create the
301
         * padding
302
         * @return xScale
303
         */
304
        public double getXScale() {
305
                return xScale;
306
        }
307
        /**
308
         * Returns the scale for the x axis of the image when it is added to create the
309
         * padding
310
         * @param xScale
311
         */
312
        public void setXScale(double xScale) {
313
                this.xScale = xScale;
314
        }
315
        /**
316
         * Defines the scale for the y axis of the image when it is added to create the
317
         * padding
318
         * @return yScale
319
         */
320
        public double getYScale() {
321
                return yScale;
322
        }
323
        /**
324
         * Returns the scale for the y axis of the image when it is added to create the
325
         * padding
326
         * @param yScale
327
         */
328
        public void setYScale(double yScale) {
329
                this.yScale = yScale;
330
        }
331

    
332
        /**
333
         * Returns the URL of the image that is used to create the padding to fill the
334
         * polygon
335
         * @return imagePath
336
         */
337
        public URL getSource() {
338
                if (bgImage != null){
339
                        return bgImage.getSource();
340
                }
341
                return null;
342
        }
343

    
344
        /**
345
         * Returns the URL of the image used when the polygon is selected
346
         * @return
347
         */
348
        public URL getSelectedSource(){
349
                if( bgSelImage != null){
350
                        return bgSelImage.getSource();
351
                }
352
                return null;
353
        }
354

    
355

    
356

    
357
        @Override
358
        public double toCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
359
                return super.toCartographicSize(viewPort, dpi, geom);
360
                // this symbol cannot apply any cartographic transfomation to its filling
361
        }
362

    
363
        public void draw(Graphics2D g, AffineTransform affineTransform,
364
                        Geometry geom, Feature f, Cancellable cancel) {
365
                draw(g, affineTransform, geom, cancel);
366

    
367
        }
368

    
369
    public Object clone() throws CloneNotSupportedException {
370
            PictureFillSymbol copy = (PictureFillSymbol) super.clone();
371

    
372
        // clone selection
373
            if (selectionSym != null) {
374
                    //Parche por lo que hace el m?todo getSymbolForSelection
375
                    if (this == selectionSym){
376
                            copy.selectionSym = copy;
377
                    } else {
378
                            copy.selectionSym = (PictureFillSymbol) selectionSym.clone();
379
                    }
380
            }
381

    
382
        // clone brackground image
383
        if (bgImage != null) {
384
            copy.bgImage = (BackgroundFileStyle) bgImage.clone();
385
        }
386

    
387
        // clone selection brackground image
388
        if (bgSelImage != null) {
389
            copy.bgSelImage = (BackgroundFileStyle) bgSelImage.clone();
390
        }
391

    
392
        // FIXME: clone properties
393

    
394
        return copy;
395
    }
396

    
397
    public void loadFromState(PersistentState state) throws PersistenceException {
398
        // Set parent style properties
399
        super.loadFromState(state);
400

    
401
        this.selected = (Boolean) state.get(SELECTED);
402
        this.selectionSym = (PictureFillSymbol) state.get(SELECTION_SYMBOL);
403
        this.bgImage = (BackgroundFileStyle) state.get(BACKGROUND_IMAGE);
404
        this.bgSelImage =
405
            (BackgroundFileStyle) state.get(BACKGROUND_SELECTION_IMAGE);
406

    
407

    
408
        this.angle = (Double) state.get(ANGLE);
409
        this.xScale = (Double) state.get(X_SCALE);
410
        this.yScale = (Double) state.get(Y_SCALE);
411
        this.markerFillProperties = (IMarkerFillPropertiesStyle) state.get(MARKER_FILL_PROPERTIES);
412

    
413

    
414
    }
415

    
416
    public void saveToState(PersistentState state) throws PersistenceException {
417
        // Save parent fill symbol properties
418
        super.saveToState(state);
419

    
420
        // Save own properties
421
        state.set(SELECTED, this.selected);
422
        state.set(SELECTION_SYMBOL, this.getSymbolForSelection());
423
        state.set(BACKGROUND_IMAGE, this.bgImage);
424
        state.set(BACKGROUND_SELECTION_IMAGE, this.bgSelImage);
425
        state.set(ANGLE, this.angle);
426
        state.set(X_SCALE, this.xScale);
427
        state.set(Y_SCALE, this.yScale);
428
        state.set(MARKER_FILL_PROPERTIES, this.markerFillProperties);
429
    }
430

    
431
    public static class RegisterPersistence implements Callable {
432

    
433
        public Object call() throws Exception {
434
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
435
            if (manager.getDefinition(PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME) == null) {
436
                DynStruct definition =
437
                    manager.addDefinition(PictureFillSymbol.class,
438
                                    PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME,
439
                                    PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME
440
                            + " Persistence definition",
441
                        null,
442
                        null);
443

    
444
                // Extend the Style base definition
445
                definition.extend(manager.getDefinition(FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME));
446

    
447
                definition.addDynFieldBoolean(SELECTED).setMandatory(false);
448
                definition.addDynFieldObject(SELECTION_SYMBOL)
449
                    .setClassOfValue(PictureFillSymbol.class).setMandatory(false);
450
                definition.addDynFieldObject(BACKGROUND_IMAGE)
451
                    .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
452
                definition.addDynFieldObject(BACKGROUND_SELECTION_IMAGE)
453
                    .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
454
                definition.addDynFieldDouble(ANGLE).setMandatory(true);
455
                definition.addDynFieldDouble(X_SCALE).setMandatory(true);
456
                definition.addDynFieldDouble(Y_SCALE).setMandatory(true);
457
                definition.addDynFieldObject(MARKER_FILL_PROPERTIES)
458
                .setClassOfValue(IMarkerFillPropertiesStyle.class).setMandatory(true);
459
            }
460
            return Boolean.TRUE;
461
        }
462
    }
463

    
464
        public static class RegisterSymbol implements Callable {
465

    
466
                public Object call() throws Exception {
467
                SymbolManager manager = MapContextLocator.getSymbolManager();
468

    
469
                manager.registerSymbol(PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME,
470
                    PictureFillSymbol.class);
471

    
472
                        return Boolean.TRUE;
473
                }
474

    
475
        }
476

    
477
        public IBackgroundFileStyle getBackgroundFileStyle() {
478
                return this.bgImage;
479
        }
480

    
481
        public IBackgroundFileStyle getSelectedBackgroundFileStyle() {
482
                return this.bgSelImage;
483
        }
484

    
485
}