Statistics
| Revision:

root / branches / v2_0_0_prep / extensions / org.gvsig.symbology / src / main / java / org / gvsig / symbology / fmap / mapcontext / rendering / symbol / style / ArrowDecoratorStyle.java @ 32880

History | View | Annotate | Download (11.9 KB)

1
/* gvSIG. Geographic Information System of the Valencian Government
2
 *
3
 * Copyright (C) 2007-2008 Infrastructures and Transports Department
4
 * of the Valencian Government (CIT)
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 2
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
 */
22
package org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style;
23

    
24
import java.awt.Graphics2D;
25
import java.awt.Rectangle;
26
import java.awt.geom.AffineTransform;
27
import java.awt.geom.Point2D;
28

    
29
import org.apache.batik.ext.awt.geom.PathLength;
30
import org.gvsig.fmap.dal.feature.Feature;
31
import org.gvsig.fmap.geom.Geometry;
32
import org.gvsig.fmap.geom.GeometryLocator;
33
import org.gvsig.fmap.geom.GeometryManager;
34
import org.gvsig.fmap.geom.Geometry.SUBTYPES;
35
import org.gvsig.fmap.geom.exception.CreateGeometryException;
36
import org.gvsig.fmap.geom.primitive.Point;
37
import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol;
38
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.line.ILineSymbol;
39
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.marker.IMarkerSymbol;
40
import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.marker.impl.ArrowMarkerSymbol;
41
import org.gvsig.tools.ToolsLocator;
42
import org.gvsig.tools.dynobject.DynStruct;
43
import org.gvsig.tools.persistence.PersistenceManager;
44
import org.gvsig.tools.persistence.PersistentState;
45
import org.gvsig.tools.persistence.exception.PersistenceException;
46

    
47
/**
48
 * Class ArrowDecoratorStyle. It is used to store the information about the
49
 * different options to draw an arrow in a line (and draw it too). This
50
 * information is taken from the panel.
51
 *
52
 * @author 2005-2008 jaume dominguez faus - jaume.dominguez@iver.es
53
 * @author 2009- <a href="cordinyana@gvsig.org">C?sar Ordi?ana</a> - gvSIG team
54
 */
55
public class ArrowDecoratorStyle extends AbstractStyle {
56
        public static final String ARROR_DECORATOR_STYLE_DYNCLASS_NAME = "ArrowDecoratorStyle";
57

    
58
        private static final String FIELD_FLIP_ALL = "flipAll";
59
        private static final String FIELD_FLIP_FIRST = "flipFirst";
60
        private static final String FIELD_ARROW_MARKER_COUNT = "arrowMarkerCount";
61
        private static final String FIELD_FOLLOW_LINE_ANGLE = "followLineAngle";
62
        private static final String FIELD_MARKER = "marker";
63

    
64
        private static final GeometryManager geomManager = GeometryLocator.getGeometryManager();
65
        private boolean flipAll = false;
66
        private boolean flipFirst = false;
67
        private int arrowMarkerCount = 2;
68
        private boolean followLineAngle = true;
69
        private IMarkerSymbol marker = new ArrowMarkerSymbol();
70

    
71
        public ArrowDecoratorStyle() {
72
                marker.setSize(10);
73
                ((ArrowMarkerSymbol) marker).setSharpness(30);
74
        }
75

    
76
        /**
77
         * Obtains the number of arrows that the user wants to draw in the same line.
78
         * @return
79
         */
80
        public int getArrowMarkerCount() {
81
                return arrowMarkerCount;
82
        }
83

    
84
        /**
85
         * Defines the number of arrows that the user wants to draw in the same line.
86
         * @return
87
         */
88
        public void setArrowMarkerCount(int arrowMarkerCount) {
89
                this.arrowMarkerCount = arrowMarkerCount;
90
        }
91

    
92
        /**
93
         * Defines the flipAll attribute.If the value of this attribute is true all the
94
         * arrows that we had drawn in the same line will be flipped.
95
         * @return
96
         */
97
        public boolean isFlipAll() {
98
                return flipAll;
99
        }
100

    
101
        /**
102
         * Obtains the flipAll attribute.If the value of this attribute is true all the
103
         * arrows that we had drawn in the same line will be flipped.
104
         * @return
105
         */
106
        public void setFlipAll(boolean flipAll) {
107
                this.flipAll = flipAll;
108
        }
109

    
110
        /**
111
         * Obtains the flipFirst attribute.If it is true only the first arrow of the line
112
         * will be flipped.The rest will keep the same orientation.
113
         * @return
114
         */
115
        public boolean isFlipFirst() {
116
                return flipFirst;
117
        }
118

    
119
        /**
120
         * Sets the flipFirst attribute.If it is true only the first arrow of the line
121
         * will be flipped.The rest will keep the same orientation.
122
         * @return
123
         */
124
        public void setFlipFirst(boolean flipFirst) {
125
                this.flipFirst = flipFirst;
126
        }
127

    
128
        /**
129
         * Gets the followLineAngle attribute.This attribute allows the arrow that we are
130
         * going to draw to be more or less aligned with the line where it will be included (depending on the angle) .
131
         * @return
132
         */
133
        public boolean isFollowLineAngle() {
134
                return followLineAngle;
135
        }
136

    
137
        /**
138
         * Sets the followLineAngle attribute.This attribute allows the arrow that we are
139
         * going to draw to be more or less aligned with the line where it will be included.
140
         * (depending on the angle).
141
         * @param followingLineAngle
142
         * @return
143
         */
144
        public void setFollowLineAngle(boolean followLineAngle) {
145
                this.followLineAngle = followLineAngle;
146
        }
147
        /**
148
         * Draws an arrow(or other symbol that substitutes an arrow selected by the user)
149
         * in a line.When the line is drawn, the symbol is added and takes care of the different
150
         * options of the user(for example if he wants to flip the first symbol or all and
151
         * the number of symbols per line to be drawn)
152
         * @param g
153
         * @param affineTransform
154
         * @param feature 
155
         * @param shp
156
         * @throws CreateGeometryException 
157
         */
158
        public void draw(Graphics2D g, AffineTransform affineTransform,
159
                        Geometry geom, Feature feature) throws CreateGeometryException {
160
                if (arrowMarkerCount <= 0) return;
161

    
162
                PathLength pl = new PathLength(geom);
163
                float size = (float) marker.getSize();
164
                float myLineLength = pl.lengthOfPath()-size; // length without the first and last arrow
165
                float step = arrowMarkerCount>2 ? myLineLength/(arrowMarkerCount-1) : pl.lengthOfPath();
166
                float rotation1; // rotation at the arrow's vertex
167
                float rotation2; // rotation at the arrow's back;
168

    
169
                Point startP;
170

    
171
                // the first arrow at the end of the line
172
                {
173
                        float theLength = pl.lengthOfPath();
174

    
175
                        if ((flipFirst || flipAll) && (flipFirst != flipAll)) { // logical XOR
176
                                Point2D p = pl.pointAtLength(theLength-size);
177
                                startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
178
                                rotation1 = pl.angleAtLength(theLength-size);
179
                                rotation2 = pl.angleAtLength(theLength);
180
                        } else {
181
                                Point2D p = pl.pointAtLength(theLength);
182
                                startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
183
                                rotation1 = pl.angleAtLength(theLength-size)+(float) Math.PI;
184
                                rotation2 = pl.angleAtLength(theLength)+(float) Math.PI;
185

    
186
                        }
187

    
188
                        if (rotation1 == rotation2)        {
189
                                marker.setRotation(rotation1);
190
                                marker.draw(g, affineTransform, startP, feature, null);
191
                        }
192
                }
193
                // the other arrows but the first and the last
194
                float aLength;
195
                for (int i = 1; i < arrowMarkerCount-1; i++) {
196
                        aLength = (float) (step*i);
197

    
198
                        rotation1 = (float) pl.angleAtLength(aLength);
199
                        rotation2 = (float) pl.angleAtLength((float)(aLength+size));
200

    
201
                        if (flipAll) {
202
                                Point2D p = pl.pointAtLength(aLength);
203
                                startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
204

    
205
                        } else {
206
                                Point2D p = pl.pointAtLength(aLength+size);
207
                                startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
208
                                rotation1 += Math.PI;
209
                                rotation2 += Math.PI;
210
                        }
211
                        /*
212
                         *  the following are just visualization improvements,
213
                         *  being rigurous it just be only these two lines
214
                         *
215
                         *  marker.setRotation(rotation1);
216
                         *  marker.draw(g, affineTransform, startP);
217
                         *
218
                         *  but it produces ugly results at the line edges
219
                         */
220
                        if (rotation1 == rotation2)        {
221
                                marker.setRotation(rotation1);
222
                                marker.draw(g, affineTransform, startP, feature, null);
223
                        } else {
224
                                rotation1 = (float) pl.angleAtLength(aLength+1);
225
                                rotation2 = (float) pl.angleAtLength((float)(aLength+size+1));
226
                                if (flipAll) {
227
                                        Point2D p = pl.pointAtLength(aLength+1);
228
                                        startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
229

    
230
                                } else {
231
                                        Point2D p = pl.pointAtLength(aLength+size+1);
232
                                        startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
233
                                        rotation1 += Math.PI;
234
                                        rotation2 += Math.PI;
235
                                }
236

    
237
                                if (rotation1 == rotation2)        {
238
                                        marker.setRotation(rotation1);
239
                                        marker.draw(g, affineTransform, startP, feature, null);
240
                                }
241
                        }
242

    
243
                }
244

    
245
                // and the last arrow at the begining of the line
246
                if (arrowMarkerCount>1) {
247
                        rotation1 = (float) pl.angleAtLength(size);
248
                        rotation2 = (float) pl.angleAtLength(0);
249

    
250
                        if (flipAll) {
251
                                Point2D p = pl.pointAtLength(0);
252
                                startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
253
                                
254
                        } else {
255
                                Point2D p = pl.pointAtLength(size);
256
                                startP = geomManager.createPoint(p.getX(), p.getY(), SUBTYPES.GEOM2D);
257
                                rotation1 += Math.PI;
258
                                rotation2 += Math.PI;
259
                        }
260

    
261
                        if (rotation1 == rotation2)        {
262
                                marker.setRotation(rotation1);
263
                                marker.draw(g, affineTransform, startP, feature, null);
264
                        }
265
                }
266
        }
267

    
268
        public void drawInsideRectangle(Graphics2D g, Rectangle r) {
269
                // TODO Auto-generated method stub
270
                throw new Error("Not yet implemented!");
271
        }
272

    
273
        public void drawOutline(Graphics2D g, Rectangle r) {
274
                // TODO Auto-generated method stub
275
                throw new Error("Not yet implemented!");
276
        }
277

    
278
        public boolean isSuitableFor(ISymbol symbol) {
279
                return symbol instanceof ILineSymbol;
280
        }
281

    
282
        public String getClassName() {
283
                return getClass().getName();
284
        }
285

    
286
        public IMarkerSymbol getMarker() {
287
                return marker;
288
        }
289

    
290
        public void setMarker(IMarkerSymbol marker) {
291
                this.marker = marker;
292
        }
293
        
294
        public Object clone() throws CloneNotSupportedException {
295
                ArrowDecoratorStyle copy  = (ArrowDecoratorStyle) super.clone();
296
                if (marker != null) {
297
                        copy.marker = (IMarkerSymbol) marker.clone();
298
                }
299
                return copy;
300
        }
301

    
302
        public void loadFromState(PersistentState state)
303
                        throws PersistenceException {
304
                // Set parent style properties
305
                super.loadFromState(state);
306

    
307
                // Set own properties
308
                setArrowMarkerCount(state.getInt(FIELD_ARROW_MARKER_COUNT));
309
                setFlipAll(state.getBoolean(FIELD_FLIP_ALL));
310
                setFlipFirst(state.getBoolean(FIELD_FLIP_FIRST));
311
                setFollowLineAngle(state.getBoolean(FIELD_FOLLOW_LINE_ANGLE));
312
                setMarker((IMarkerSymbol) state.get(FIELD_MARKER));
313
        }
314

    
315
        public void saveToState(PersistentState state) throws PersistenceException {
316
                // Save parent fill symbol properties
317
                super.saveToState(state);
318

    
319
                // Save own properties
320
                state.set(FIELD_ARROW_MARKER_COUNT, getArrowMarkerCount());
321
                state.set(FIELD_FLIP_ALL, isFlipAll());
322
                state.set(FIELD_FLIP_FIRST, isFlipFirst());
323
                state.set(FIELD_FOLLOW_LINE_ANGLE, isFollowLineAngle());
324
                state.set(FIELD_MARKER, getMarker());
325
        }
326

    
327
        public static void registerPersistence() {
328
                // Add the ArrowDecoratorStyle DynClass definition.
329
                PersistenceManager manager = ToolsLocator.getPersistenceManager();
330
                DynStruct definition = manager.addDefinition(
331
                                ArrowDecoratorStyle.class,
332
                                ARROR_DECORATOR_STYLE_DYNCLASS_NAME,
333
                                ARROR_DECORATOR_STYLE_DYNCLASS_NAME+" Persistence definition",
334
                                null, 
335
                                null
336
                );
337
                
338
                // Extend the Style base definition
339
                definition.extend(STYLE_DYNCLASS_NAME);
340

    
341
                // Arrow marker count
342
                definition.addDynFieldInt(FIELD_ARROW_MARKER_COUNT).setMandatory(true);
343
                // Flip all
344
                definition.addDynFieldBoolean(FIELD_FLIP_ALL).setMandatory(true);
345
                // flip first
346
                definition.addDynFieldBoolean(FIELD_FLIP_FIRST).setMandatory(true);
347
                // Follow line angles
348
                definition.addDynFieldBoolean(FIELD_FOLLOW_LINE_ANGLE).setMandatory(true);
349
                // Marker
350
                definition.addDynFieldObject(FIELD_MARKER).setMandatory(true);
351

    
352
        }
353

    
354
}