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 / line / impl / PictureLineSymbol.java @ 40560

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

    
26
import java.awt.BasicStroke;
27
import java.awt.Color;
28
import java.awt.Graphics2D;
29
import java.awt.Rectangle;
30
import java.awt.Shape;
31
import java.awt.geom.AffineTransform;
32
import java.awt.geom.PathIterator;
33
import java.awt.geom.Point2D;
34
import java.io.IOException;
35
import java.net.URL;
36

    
37
import org.apache.batik.ext.awt.geom.PathLength;
38
import org.gvsig.compat.print.PrintAttributes;
39
import org.gvsig.fmap.dal.feature.Feature;
40
import org.gvsig.fmap.geom.Geometry;
41
import org.gvsig.fmap.mapcontext.MapContextLocator;
42
import org.gvsig.fmap.mapcontext.ViewPort;
43
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
44
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolDrawingException;
45
import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager;
46
import org.gvsig.i18n.Messages;
47
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.CartographicSupportToolkit;
48
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.line.IPictureLineSymbol;
49
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.BackgroundFileStyle;
50
import org.gvsig.tools.ToolsLocator;
51
import org.gvsig.tools.dynobject.DynStruct;
52
import org.gvsig.tools.persistence.PersistenceManager;
53
import org.gvsig.tools.persistence.PersistentState;
54
import org.gvsig.tools.persistence.exception.PersistenceException;
55
import org.gvsig.tools.task.Cancellable;
56
import org.gvsig.tools.util.Callable;
57
import org.slf4j.Logger;
58
import org.slf4j.LoggerFactory;
59

    
60

    
61
/**
62
 * PictureLineSymbol allows to use any symbol defined as an image (by an image file)
63
 * supported  by gvSIG.This symbol will be used as an initial object.The line will be
64
 * painted as a succession of puntual symbols through the path defined by it(the line).
65
 *
66
 * @author jaume dominguez faus - jaume.dominguez@iver.es
67
 */
68
public class PictureLineSymbol extends AbstractLineSymbol implements IPictureLineSymbol  {
69

    
70
        private static final Logger logger = LoggerFactory.getLogger(PictureLineSymbol.class);
71

    
72
    public static final String PICTURE_LINE_SYMBOL_PERSISTENCE_DEFINITION_NAME =
73
        "PictureLineSymbol";
74
    private static final String SELECTED = "selected";
75
    private static final String SELECTION_SYMBOL = "selectionSym";
76
    private static final String BACKGROUND_IMAGE = "bgImage";
77
    private static final String BACKGROUND_SELECTION_IMAGE = "bgSelImage";
78
    private static final String WIDTH = "width";
79

    
80
        transient private PictureLineSymbol selectionSym;
81
        private double width;
82
        private boolean selected;
83
        private double xScale = 1, csXScale = xScale;
84
        private double yScale = 1, csYScale = yScale;
85

    
86
        private BackgroundFileStyle bgImage;
87
        private BackgroundFileStyle bgSelImage;
88
        private PrintAttributes properties;
89

    
90
        /**
91
         * Constructor method
92
         *
93
         */
94
        public PictureLineSymbol() {
95
                super();
96
        }
97
        /**
98
         * Constructor method
99
         * @param imageURL, URL of the normal image
100
         * @param selImageURL, URL of the image when it is selected in the map
101
         * @throws IOException
102
         */
103

    
104
        public PictureLineSymbol(URL imageURL, URL selImageURL) throws IOException {
105
                setImage(imageURL);
106
                if (selImageURL!=null)
107
                        setSelImage(selImageURL);
108
                else setSelImage(imageURL);
109
        }
110
        /**
111
         * Sets the URL for the image to be used as a picture line symbol
112
         * @param imageFile, File
113
         * @throws IOException
114
         */
115
        public void setImage(URL imageUrl) throws IOException{
116

    
117
                bgImage= BackgroundFileStyle.createStyleByURL(imageUrl);
118
        }
119
        /**
120
         * Sets the URL for the image to be used as a picture line symbol (when it is selected in the map)
121
         * @param imageFile, File
122
         * @throws IOException
123
         */
124
        public void setSelImage(URL selImageUrl) throws IOException{
125

    
126
                bgSelImage= BackgroundFileStyle.createStyleByURL(selImageUrl);
127
        }
128

    
129

    
130
        public void setLineWidth(double width) {
131
                this.width = width;
132
                getLineStyle().setLineWidth((float) width);
133
        }
134

    
135
        public double getLineWidth() {
136
                return width;
137
        }
138

    
139
        public ISymbol getSymbolForSelection() {
140
                if (selectionSym == null) {
141
                        selectionSym = (PictureLineSymbol) cloneForSelection();
142
                        selectionSym.selected=true;
143
                        selectionSym.selectionSym = selectionSym; // avoid too much lazy creations
144

    
145
                }
146
                return selectionSym;
147

    
148
        }
149

    
150
        public void draw(Graphics2D g, AffineTransform affineTransform,        Geometry geom, Feature f, Cancellable cancel) {
151
                draw(g, affineTransform, geom, cancel);
152
        }
153

    
154
        private void draw(Graphics2D g, AffineTransform affineTransform, Geometry geom, Cancellable cancel) {
155

    
156
            if (csXScale<=0 && csYScale<=0) {
157
                return;
158
            }
159

    
160
            float csWidth = getLineStyle().getLineWidth();
161
                
162
                BasicStroke bs = new BasicStroke(
163
                    (float) csWidth,
164
                    BasicStroke.CAP_ROUND,
165
                    BasicStroke.CAP_ROUND);
166
                
167
                Shape geom_transf_clip = geom.getShape(affineTransform);
168
                g.setClip(bs.createStrokedShape(geom_transf_clip));
169
                
170
                BackgroundFileStyle bg = (!selected) ? bgImage : bgSelImage ;
171

    
172
                Rectangle bounds = bg.getBounds();
173
                final double imageWidth  = bounds.getWidth()  * csXScale;
174
                final double imageHeight = bounds.getHeight() * csYScale;
175

    
176
                if (imageWidth==0 || imageHeight==0) return;
177
                int height = (int) csWidth;
178

    
179
                PathLength pl = new PathLength(geom_transf_clip);
180
                PathIterator iterator = geom_transf_clip.getPathIterator(null, 0.8);
181
                double[] theData = new double[6];
182
                Point2D firstPoint = null, startPoint = null, endPoint = null;
183
                if (!iterator.isDone()) {
184
                        if ( iterator.currentSegment(theData) != PathIterator.SEG_CLOSE) {
185
                                firstPoint = new Point2D.Double(theData[0], theData[1]);
186
                        }
187
                }
188
                float currentPathLength = 1;
189

    
190
                Rectangle rect = new Rectangle();
191

    
192
                while ((cancel==null || !cancel.isCanceled()) && !iterator.isDone()) {
193

    
194
                        int theType = iterator.currentSegment(theData);
195
                        switch (theType) {
196
                        case PathIterator.SEG_MOVETO:
197
                                startPoint = new Point2D.Double(theData[0], theData[1]);
198

    
199
                                endPoint = null;
200
                                iterator.next();
201

    
202
                                continue;
203

    
204
                        case PathIterator.SEG_LINETO:
205
                        case PathIterator.SEG_QUADTO:
206
                        case PathIterator.SEG_CUBICTO:
207
                                endPoint = new Point2D.Double(theData[0], theData[1]);
208

    
209
                                break;
210
                        case PathIterator.SEG_CLOSE:
211
                                endPoint = startPoint;
212
                                startPoint = firstPoint;
213
                                break;
214
                        }
215

    
216
                        double a = endPoint.getX() - startPoint.getX();
217
                        double b = endPoint.getY() - startPoint.getY();
218
                        double theta = pl.angleAtLength(currentPathLength);
219

    
220
                        double x = startPoint.getX();
221
                        double y = startPoint.getY();
222

    
223
                        // Theorem of Pythagoras
224
                        float segmentLength = (float) Math.sqrt(a*a + b*b);
225

    
226
                        // compute how many times the image has to be drawn
227
                        // to completely cover this segment's length
228
                        int count = (int) Math.ceil(segmentLength/imageWidth);
229

    
230
                        for (int i = 0; (cancel==null || !cancel.isCanceled()) && i < count; i++) {
231
                                g.translate(x, y);
232
                                g.rotate(theta);
233

    
234
                                double xOffsetTranslation = imageWidth*i;
235
                                g.translate(xOffsetTranslation, -csWidth);
236

    
237
                                rect.setBounds(0, (int) Math.round(height*.5), (int) Math.ceil(imageWidth), height);
238
                                try {
239
                                        bg.drawInsideRectangle(g, rect, false);
240
                                } catch (SymbolDrawingException e) {
241
                                        logger.warn(Messages.getText("label_style_could_not_be_painted"), e);
242
                                }
243
                                g.translate(-xOffsetTranslation, csWidth);
244

    
245
                                g.rotate(-theta);
246
                                g.translate(-x, -y);
247
                        }
248

    
249
                        startPoint = endPoint;
250
                        currentPathLength += segmentLength;
251
                        iterator.next();
252
                }
253
                g.setClip(null);
254
        }
255

    
256
        /**
257
         * Sets the yscale for the picture line symbol
258
         * @param yScale
259
         */
260
        public void setYScale(double yScale) {
261
                this.yScale = yScale;
262
                this.csYScale = yScale;
263
        }
264
        /**
265
         * Sets the xscale for the picture line symbol
266
         * @param xScale
267
         */
268
        public void setXScale(double xScale) {
269
                this.xScale = xScale;
270
                this.csXScale = xScale;
271
        }
272

    
273
        public String getClassName() {
274
                return getClass().getName();
275
        }
276

    
277
        public void print(Graphics2D g, AffineTransform at, Geometry geom,
278
                        PrintAttributes properties) {
279
                this.properties=properties;
280
        draw(g, at, geom, null);
281
        this.properties=null;
282

    
283
        }
284
        
285
        /**
286
         * Returns the URL of the image that is used as a picture line symbol (when it
287
         * is selected in the map)
288
         * @return selimagePath,URL
289
         */
290
        public URL getSelectedSource(){
291
                return bgSelImage.getSource();
292
        }
293
        /**
294
         * Returns the URL of the image that is used as a picture line symbol
295
         * @return imagePath,URL
296
         */
297
        public URL getSource() {
298
                return bgImage.getSource();
299
        }
300
        
301
        /**
302
         * Returns the xscale for the picture line symbol
303
         * @param xScale
304
         */
305
        public double getXScale() {
306
                return xScale;
307
        }
308
        /**
309
         * Returns the yscale for the picture line symbol
310
         * @param yScale
311
         */
312
        public double getYScale() {
313
                return yScale;
314
        }
315

    
316
        @Override
317
        public void setCartographicSize(double cartographicSize, Geometry geom) {
318
                getLineStyle().setLineWidth((float) cartographicSize);
319
                double scale = cartographicSize/width;
320
                csXScale = xScale * scale;
321
                csYScale = yScale * scale;
322
        }
323

    
324
        @Override
325
        public double toCartographicSize(ViewPort viewPort, double dpi, Geometry geom) {
326
                double s = super.toCartographicSize(viewPort, dpi, geom);
327
                setCartographicSize(CartographicSupportToolkit.
328
                                getCartographicLength(this, width, viewPort, dpi), geom);
329
                return s;
330
        }
331
        
332
        
333
    public Object clone() throws CloneNotSupportedException {
334
        PictureLineSymbol copy = (PictureLineSymbol) super.clone();
335

    
336
        // clone selection
337
        if (selectionSym != null) {
338
                //to avoid an infinite loop
339
                if (this == selectionSym){
340
                        copy.selectionSym = copy;
341
                } else {
342
                        copy.selectionSym = (PictureLineSymbol) selectionSym.clone();
343
                }
344
        }
345

    
346
        // clone brackground image
347
        if (bgImage != null) {
348
            copy.bgImage = (BackgroundFileStyle) bgImage.clone();
349
        }
350

    
351
        // clone selection brackground image
352
        if (bgSelImage != null) {
353
            copy.bgSelImage = (BackgroundFileStyle) bgSelImage.clone();
354
        }
355

    
356
        // FIXME: clone properties
357
        
358
        return copy;
359
    }
360

    
361
    public void loadFromState(PersistentState state) throws PersistenceException {
362
        // Set parent style properties
363
        super.loadFromState(state);
364

    
365
        this.selected = (Boolean) state.get(SELECTED);
366
        this.selectionSym = (PictureLineSymbol) state.get(SELECTION_SYMBOL);
367
        this.bgImage = (BackgroundFileStyle) state.get(BACKGROUND_IMAGE);
368
        this.bgSelImage =
369
            (BackgroundFileStyle) state.get(BACKGROUND_SELECTION_IMAGE);
370
        this.setLineWidth(state.getDouble(WIDTH));
371
    }
372

    
373
    public void saveToState(PersistentState state) throws PersistenceException {
374
        // Save parent fill symbol properties
375
        super.saveToState(state);
376

    
377
        // Save own properties
378
        state.set(SELECTED, this.selected);
379
        state.set(SELECTION_SYMBOL, this.getSymbolForSelection());
380
        state.set(BACKGROUND_IMAGE, this.bgImage);
381
        state.set(BACKGROUND_SELECTION_IMAGE, this.bgSelImage);
382
        state.set(WIDTH, width);
383
    }
384

    
385
    public static class RegisterPersistence implements Callable {
386

    
387
        public Object call() throws Exception {
388
            PersistenceManager manager = ToolsLocator.getPersistenceManager();
389
            if (manager.getDefinition(PICTURE_LINE_SYMBOL_PERSISTENCE_DEFINITION_NAME) == null) {
390
                DynStruct definition =
391
                    manager.addDefinition(PictureLineSymbol.class,
392
                                    PICTURE_LINE_SYMBOL_PERSISTENCE_DEFINITION_NAME,
393
                                    PICTURE_LINE_SYMBOL_PERSISTENCE_DEFINITION_NAME
394
                            + " Persistence definition",
395
                        null,
396
                        null);
397

    
398
                // Extend the Style base definition
399
                definition.extend(manager.getDefinition(LINE_SYMBOL_PERSISTENCE_DEFINITION_NAME));
400

    
401
                definition.addDynFieldBoolean(SELECTED).setMandatory(false);
402
                definition.addDynFieldObject(SELECTION_SYMBOL)
403
                    .setClassOfValue(PictureLineSymbol.class).setMandatory(false);
404
                definition.addDynFieldObject(BACKGROUND_IMAGE)
405
                    .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
406
                definition.addDynFieldObject(BACKGROUND_SELECTION_IMAGE)
407
                    .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
408
                
409
                definition.addDynFieldObject(WIDTH)
410
                    .setClassOfValue(Double.class).setMandatory(true);
411
            }
412
            return Boolean.TRUE;
413
        }
414
    }
415
    
416
        public static class RegisterSymbol implements Callable {
417

    
418
                public Object call() throws Exception {
419
                        SymbolManager manager = MapContextLocator.getSymbolManager();
420

    
421
                        manager.registerSymbol(PICTURE_LINE_SYMBOL_PERSISTENCE_DEFINITION_NAME,
422
                            PictureLineSymbol.class);
423

    
424
                        return Boolean.TRUE;
425
                }
426
        }
427
}