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

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.Graphics2D;
28
import java.awt.Rectangle;
29
import java.awt.Shape;
30
import java.awt.geom.AffineTransform;
31
import java.awt.geom.PathIterator;
32
import java.awt.geom.Point2D;
33
import java.io.IOException;
34
import java.net.URL;
35

    
36
import org.apache.batik.ext.awt.geom.DefaultPathLength;
37
import org.gvsig.compat.print.PrintAttributes;
38
import org.gvsig.fmap.dal.feature.Feature;
39
import org.gvsig.fmap.geom.Geometry;
40
import org.gvsig.fmap.mapcontext.MapContext;
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.PathLength;
48
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.impl.CartographicSupportToolkit;
49
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.line.IPictureLineSymbol;
50
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.BackgroundFileStyle;
51
import org.gvsig.tools.ToolsLocator;
52
import org.gvsig.tools.dynobject.DynStruct;
53
import org.gvsig.tools.persistence.PersistenceManager;
54
import org.gvsig.tools.persistence.PersistentState;
55
import org.gvsig.tools.persistence.exception.PersistenceException;
56
import org.gvsig.tools.task.Cancellable;
57
import org.gvsig.tools.util.Callable;
58
import org.slf4j.Logger;
59
import org.slf4j.LoggerFactory;
60

    
61

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

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

    
71
    public static final String PICTURE_LINE_SYMBOL_PERSISTENCE_DEFINITION_NAME =
72
        "PictureLineSymbol";
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 WIDTH = "width";
78

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

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

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

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

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

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

    
128

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

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

    
138
        public ISymbol getSymbolForSelection() {
139
                if (selectionSym == null) {
140
                        selectionSym = (PictureLineSymbol) cloneForSelection();
141
                        selectionSym.selected=true;
142
                        selectionSym.selectionSym = selectionSym; // avoid too much lazy creations
143
                }else{
144
                    selectionSym.setColor(MapContext.getSelectionColor());
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 DefaultPathLength(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
}