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 @ 40560
History | View | Annotate | Download (15.4 KB)
1 | 40560 | jjdelcerro | /**
|
---|---|---|---|
2 | * gvSIG. Desktop Geographic Information System.
|
||
3 | 40435 | jjdelcerro | *
|
4 | 40560 | jjdelcerro | * Copyright (C) 2007-2013 gvSIG Association.
|
5 | 40435 | jjdelcerro | *
|
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 | 40560 | jjdelcerro | * as published by the Free Software Foundation; either version 3
|
9 | 40435 | jjdelcerro | * 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 | 40560 | jjdelcerro | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
19 | * MA 02110-1301, USA.
|
||
20 | 40435 | jjdelcerro | *
|
21 | 40560 | jjdelcerro | * For any additional information, do not hesitate to contact us
|
22 | * at info AT gvsig.com, or visit our website www.gvsig.com.
|
||
23 | 40435 | jjdelcerro | */
|
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.MapContextLocator; |
||
50 | import org.gvsig.fmap.mapcontext.ViewPort; |
||
51 | import org.gvsig.fmap.mapcontext.rendering.symbols.ISymbol; |
||
52 | import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolDrawingException; |
||
53 | import org.gvsig.fmap.mapcontext.rendering.symbols.SymbolManager; |
||
54 | import org.gvsig.i18n.Messages; |
||
55 | import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.fill.IPictureFillSymbol; |
||
56 | import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.BackgroundFileStyle; |
||
57 | import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.IMarkerFillPropertiesStyle; |
||
58 | import org.gvsig.symbology.fmap.mapcontext.rendering.symbol.style.SimpleMarkerFillPropertiesStyle; |
||
59 | import org.gvsig.tools.ToolsLocator; |
||
60 | import org.gvsig.tools.dynobject.DynStruct; |
||
61 | import org.gvsig.tools.persistence.PersistenceManager; |
||
62 | import org.gvsig.tools.persistence.PersistentState; |
||
63 | import org.gvsig.tools.persistence.exception.PersistenceException; |
||
64 | import org.gvsig.tools.task.Cancellable; |
||
65 | import org.gvsig.tools.util.Callable; |
||
66 | import org.slf4j.Logger; |
||
67 | import org.slf4j.LoggerFactory; |
||
68 | |||
69 | /**
|
||
70 | * PictureFillSymbol allows to use an image file suported by gvSIG as a padding
|
||
71 | * for the polygons.This image can be modified using methods to scale or rotate it.
|
||
72 | * @author jaume dominguez faus - jaume.dominguez@iver.es
|
||
73 | */
|
||
74 | public class PictureFillSymbol extends AbstractFillSymbol implements IPictureFillSymbol { |
||
75 | private static final Logger logger = LoggerFactory.getLogger(PictureFillSymbol.class); |
||
76 | |||
77 | public static final String PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME = |
||
78 | "PictureFillSymbol";
|
||
79 | private static final String SELECTED = "selected"; |
||
80 | private static final String SELECTION_SYMBOL = "selectionSym"; |
||
81 | private static final String BACKGROUND_IMAGE = "bgImage"; |
||
82 | private static final String BACKGROUND_SELECTION_IMAGE = "bgSelImage"; |
||
83 | private static final String ANGLE = "angle"; |
||
84 | private static final String X_SCALE = "xScale"; |
||
85 | private static final String Y_SCALE = "yScale"; |
||
86 | private static final String MARKER_FILL_PROPERTIES = "markerFillProperties"; |
||
87 | |||
88 | private double angle = 0; |
||
89 | private double xScale = 1; |
||
90 | private double yScale = 1; |
||
91 | transient private PictureFillSymbol selectionSym; |
||
92 | private boolean selected; |
||
93 | private IMarkerFillPropertiesStyle markerFillProperties = new SimpleMarkerFillPropertiesStyle(); |
||
94 | private BackgroundFileStyle bgImage;
|
||
95 | private BackgroundFileStyle bgSelImage;
|
||
96 | private PrintAttributes properties;
|
||
97 | private GeometryManager geometryManager = GeometryLocator.getGeometryManager();
|
||
98 | |||
99 | private void draw(Graphics2D g, AffineTransform affineTransform, Geometry geom, Cancellable cancel) { |
||
100 | Color fillColor = getFillColor();
|
||
101 | |||
102 | Shape transf_geom = geom.getShape(affineTransform);
|
||
103 | |||
104 | if (fillColor != null) { |
||
105 | g.setColor(fillColor); |
||
106 | g.fill(transf_geom); |
||
107 | } |
||
108 | |||
109 | g.setClip(transf_geom); |
||
110 | BackgroundFileStyle bg = (!selected) ? bgImage : bgSelImage ; |
||
111 | if (bg != null && bg.getBounds() != null){ |
||
112 | int sizeW=(int) ((int)bg.getBounds().getWidth() * xScale); |
||
113 | int sizeH=(int) ((int)bg.getBounds().getHeight() * yScale); |
||
114 | Rectangle rProv = new Rectangle(); |
||
115 | rProv.setFrame(0,0,sizeW,sizeH); |
||
116 | Paint resulPatternFill = null; |
||
117 | BufferedImage sample= null; |
||
118 | |||
119 | sample= new BufferedImage(sizeW,sizeH,BufferedImage.TYPE_INT_ARGB); |
||
120 | Graphics2D gAux = sample.createGraphics();
|
||
121 | |||
122 | double xSeparation = markerFillProperties.getXSeparation(); // TODO apply CartographicSupport |
||
123 | double ySeparation = markerFillProperties.getYSeparation(); // TODO apply CartographicSupport |
||
124 | double xOffset = markerFillProperties.getXOffset();
|
||
125 | double yOffset = markerFillProperties.getYOffset();
|
||
126 | |||
127 | try {
|
||
128 | bg.drawInsideRectangle(gAux, rProv); |
||
129 | } catch (SymbolDrawingException e) {
|
||
130 | logger.warn(Messages.getText("label_style_could_not_be_painted"), e);
|
||
131 | } |
||
132 | |||
133 | Dimension sz = rProv.getSize();
|
||
134 | sz = new Dimension((int) Math.round(sz.getWidth()+(xSeparation)), |
||
135 | (int) Math.round(sz.getHeight() + (ySeparation))); |
||
136 | rProv.setSize(sz); |
||
137 | |||
138 | BufferedImage bi = new BufferedImage(rProv.width, rProv.height, BufferedImage.TYPE_INT_ARGB); |
||
139 | gAux = bi.createGraphics(); |
||
140 | gAux.drawImage(sample, null, (int) (xSeparation*0.5), (int) (ySeparation*0.5)); |
||
141 | |||
142 | rProv.x = rProv.x+(int)xOffset;
|
||
143 | rProv.y = rProv.y+(int)yOffset;
|
||
144 | resulPatternFill = new TexturePaint(bi,rProv); |
||
145 | |||
146 | g.setColor(null);
|
||
147 | g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
|
||
148 | RenderingHints.VALUE_ANTIALIAS_ON);
|
||
149 | |||
150 | g.setPaint(resulPatternFill); |
||
151 | } |
||
152 | |||
153 | if (angle == 0) { |
||
154 | g.fill(transf_geom); |
||
155 | } else {
|
||
156 | |||
157 | AffineTransform at = new AffineTransform(); |
||
158 | at.rotate(angle); |
||
159 | |||
160 | Geometry pixels_geom = geom.cloneGeometry(); |
||
161 | pixels_geom.transform(affineTransform); |
||
162 | pixels_geom.transform(at); |
||
163 | |||
164 | g.rotate(-angle); |
||
165 | g.fill(pixels_geom.getShape()); |
||
166 | g.rotate(angle); |
||
167 | } |
||
168 | |||
169 | g.setClip(null);
|
||
170 | if (getOutline()!=null) { |
||
171 | getOutline().draw(g, affineTransform, geom, null, cancel);
|
||
172 | } |
||
173 | } |
||
174 | |||
175 | /**
|
||
176 | * Constructor method
|
||
177 | *
|
||
178 | */
|
||
179 | public PictureFillSymbol() {
|
||
180 | super();
|
||
181 | } |
||
182 | /**
|
||
183 | * Constructor method
|
||
184 | * @param imageURL, URL of the normal image
|
||
185 | * @param selImageURL, URL of the image when it is selected in the map
|
||
186 | * @throws IOException
|
||
187 | */
|
||
188 | |||
189 | public PictureFillSymbol(URL imageURL, URL selImageURL) throws IOException { |
||
190 | setImage(imageURL); |
||
191 | if (selImageURL!=null) |
||
192 | setSelImage(selImageURL); |
||
193 | else setSelImage(imageURL);
|
||
194 | } |
||
195 | |||
196 | |||
197 | /**
|
||
198 | * Sets the URL for the image to be used as a picture fill symbol (when it is selected in the map)
|
||
199 | * @param imageFile, File
|
||
200 | * @throws IOException
|
||
201 | */
|
||
202 | public void setSelImage(URL selImageUrl) throws IOException{ |
||
203 | |||
204 | bgSelImage= BackgroundFileStyle.createStyleByURL(selImageUrl); |
||
205 | // selImagePath = selImageUrl.toString();
|
||
206 | } |
||
207 | |||
208 | |||
209 | |||
210 | /**
|
||
211 | * Defines the URL from where the picture to fill the polygon is taken.
|
||
212 | * @param imageFile
|
||
213 | * @throws IOException
|
||
214 | */
|
||
215 | public void setImage(URL imageUrl) throws IOException{ |
||
216 | bgImage = BackgroundFileStyle.createStyleByURL(imageUrl); |
||
217 | } |
||
218 | |||
219 | |||
220 | |||
221 | public void drawInsideRectangle(Graphics2D g, |
||
222 | AffineTransform scaleInstance, Rectangle r, PrintAttributes properties) throws SymbolDrawingException { |
||
223 | Surface surf; |
||
224 | try {
|
||
225 | surf = geometryManager.createSurface(new GeneralPathX(r.getPathIterator(new AffineTransform())), SUBTYPES.GEOM2D); |
||
226 | if (properties==null){ |
||
227 | // draw(g, null, new FPolygon2D(new GeneralPathX(r)), null);
|
||
228 | draw(g, null, surf, null); |
||
229 | } else {
|
||
230 | print(g, new AffineTransform(), surf, properties); |
||
231 | } |
||
232 | } catch (CreateGeometryException e) {
|
||
233 | throw new SymbolDrawingException(TYPES.POINT); |
||
234 | } |
||
235 | } |
||
236 | |||
237 | public ISymbol getSymbolForSelection() {
|
||
238 | if (selectionSym == null) { |
||
239 | selectionSym = (PictureFillSymbol) cloneForSelection(); |
||
240 | selectionSym.selected=true;
|
||
241 | selectionSym.selectionSym = selectionSym; // avoid too much lazy creations
|
||
242 | } |
||
243 | return selectionSym;
|
||
244 | |||
245 | } |
||
246 | |||
247 | public int getSymbolType() { |
||
248 | return Geometry.TYPES.SURFACE;
|
||
249 | } |
||
250 | |||
251 | public String getClassName() { |
||
252 | return getClass().getName();
|
||
253 | } |
||
254 | |||
255 | public void print(Graphics2D g, AffineTransform at, Geometry geom, |
||
256 | PrintAttributes properties) { |
||
257 | this.properties=properties;
|
||
258 | draw(g, at, geom, null);
|
||
259 | this.properties=null; |
||
260 | } |
||
261 | /**
|
||
262 | * Returns the IMarkerFillProperties that allows this class to treat the picture as
|
||
263 | * a marker in order to scale it, rotate it and so on.
|
||
264 | * @return markerFillProperties,IMarkerFillPropertiesStyle
|
||
265 | */
|
||
266 | public IMarkerFillPropertiesStyle getMarkerFillProperties() {
|
||
267 | return markerFillProperties;
|
||
268 | } |
||
269 | |||
270 | /**
|
||
271 | * Sets the MarkerFillProperties in order to allow the user to modify the picture as
|
||
272 | * a marker (it is possible to scale it, rotate it and so on)
|
||
273 | * @param prop
|
||
274 | */
|
||
275 | public void setMarkerFillProperties(IMarkerFillPropertiesStyle prop) { |
||
276 | this.markerFillProperties = prop;
|
||
277 | } |
||
278 | /**
|
||
279 | * Defines the angle for the rotation of the image when it is added to create the
|
||
280 | * padding
|
||
281 | *
|
||
282 | * @return angle
|
||
283 | */
|
||
284 | public double getAngle() { |
||
285 | return angle;
|
||
286 | } |
||
287 | /**
|
||
288 | * Sets the angle for the rotation of the image when it is added to create the padding
|
||
289 | * @param angle
|
||
290 | */
|
||
291 | public void setAngle(double angle) { |
||
292 | this.angle = angle;
|
||
293 | } |
||
294 | |||
295 | /**
|
||
296 | * Defines the scale for the x axis of the image when it is added to create the
|
||
297 | * padding
|
||
298 | * @return xScale
|
||
299 | */
|
||
300 | public double getXScale() { |
||
301 | return xScale;
|
||
302 | } |
||
303 | /**
|
||
304 | * Returns the scale for the x axis of the image when it is added to create the
|
||
305 | * padding
|
||
306 | * @param xScale
|
||
307 | */
|
||
308 | public void setXScale(double xScale) { |
||
309 | this.xScale = xScale;
|
||
310 | } |
||
311 | /**
|
||
312 | * Defines the scale for the y axis of the image when it is added to create the
|
||
313 | * padding
|
||
314 | * @return yScale
|
||
315 | */
|
||
316 | public double getYScale() { |
||
317 | return yScale;
|
||
318 | } |
||
319 | /**
|
||
320 | * Returns the scale for the y axis of the image when it is added to create the
|
||
321 | * padding
|
||
322 | * @param yScale
|
||
323 | */
|
||
324 | public void setYScale(double yScale) { |
||
325 | this.yScale = yScale;
|
||
326 | } |
||
327 | |||
328 | /**
|
||
329 | * Returns the URL of the image that is used to create the padding to fill the
|
||
330 | * polygon
|
||
331 | * @return imagePath
|
||
332 | */
|
||
333 | public URL getSource() { |
||
334 | if (bgImage != null){ |
||
335 | return bgImage.getSource();
|
||
336 | } |
||
337 | return null; |
||
338 | } |
||
339 | |||
340 | /**
|
||
341 | * Returns the URL of the image used when the polygon is selected
|
||
342 | * @return
|
||
343 | */
|
||
344 | public URL getSelectedSource(){ |
||
345 | if( bgSelImage != null){ |
||
346 | return bgSelImage.getSource();
|
||
347 | } |
||
348 | return null; |
||
349 | } |
||
350 | |||
351 | |||
352 | |||
353 | @Override
|
||
354 | public double toCartographicSize(ViewPort viewPort, double dpi, Geometry geom) { |
||
355 | return super.toCartographicSize(viewPort, dpi, geom); |
||
356 | // this symbol cannot apply any cartographic transfomation to its filling
|
||
357 | } |
||
358 | |||
359 | public void draw(Graphics2D g, AffineTransform affineTransform, |
||
360 | Geometry geom, Feature f, Cancellable cancel) { |
||
361 | draw(g, affineTransform, geom, cancel); |
||
362 | |||
363 | } |
||
364 | |||
365 | public Object clone() throws CloneNotSupportedException { |
||
366 | PictureFillSymbol copy = (PictureFillSymbol) super.clone();
|
||
367 | |||
368 | // clone selection
|
||
369 | if (selectionSym != null) { |
||
370 | //Parche por lo que hace el m?todo getSymbolForSelection
|
||
371 | if (this == selectionSym){ |
||
372 | copy.selectionSym = copy; |
||
373 | } else {
|
||
374 | copy.selectionSym = (PictureFillSymbol) selectionSym.clone(); |
||
375 | } |
||
376 | } |
||
377 | |||
378 | // clone brackground image
|
||
379 | if (bgImage != null) { |
||
380 | copy.bgImage = (BackgroundFileStyle) bgImage.clone(); |
||
381 | } |
||
382 | |||
383 | // clone selection brackground image
|
||
384 | if (bgSelImage != null) { |
||
385 | copy.bgSelImage = (BackgroundFileStyle) bgSelImage.clone(); |
||
386 | } |
||
387 | |||
388 | // FIXME: clone properties
|
||
389 | |||
390 | return copy;
|
||
391 | } |
||
392 | |||
393 | public void loadFromState(PersistentState state) throws PersistenceException { |
||
394 | // Set parent style properties
|
||
395 | super.loadFromState(state);
|
||
396 | |||
397 | this.selected = (Boolean) state.get(SELECTED); |
||
398 | this.selectionSym = (PictureFillSymbol) state.get(SELECTION_SYMBOL);
|
||
399 | this.bgImage = (BackgroundFileStyle) state.get(BACKGROUND_IMAGE);
|
||
400 | this.bgSelImage =
|
||
401 | (BackgroundFileStyle) state.get(BACKGROUND_SELECTION_IMAGE); |
||
402 | |||
403 | |||
404 | this.angle = (Double) state.get(ANGLE); |
||
405 | this.xScale = (Double) state.get(X_SCALE); |
||
406 | this.yScale = (Double) state.get(Y_SCALE); |
||
407 | this.markerFillProperties = (IMarkerFillPropertiesStyle) state.get(MARKER_FILL_PROPERTIES);
|
||
408 | |||
409 | |||
410 | } |
||
411 | |||
412 | public void saveToState(PersistentState state) throws PersistenceException { |
||
413 | // Save parent fill symbol properties
|
||
414 | super.saveToState(state);
|
||
415 | |||
416 | // Save own properties
|
||
417 | state.set(SELECTED, this.selected);
|
||
418 | state.set(SELECTION_SYMBOL, this.getSymbolForSelection());
|
||
419 | state.set(BACKGROUND_IMAGE, this.bgImage);
|
||
420 | state.set(BACKGROUND_SELECTION_IMAGE, this.bgSelImage);
|
||
421 | state.set(ANGLE, this.angle);
|
||
422 | state.set(X_SCALE, this.xScale);
|
||
423 | state.set(Y_SCALE, this.yScale);
|
||
424 | state.set(MARKER_FILL_PROPERTIES, this.markerFillProperties);
|
||
425 | } |
||
426 | |||
427 | public static class RegisterPersistence implements Callable { |
||
428 | |||
429 | public Object call() throws Exception { |
||
430 | PersistenceManager manager = ToolsLocator.getPersistenceManager(); |
||
431 | if (manager.getDefinition(PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME) == null) { |
||
432 | DynStruct definition = |
||
433 | manager.addDefinition(PictureFillSymbol.class, |
||
434 | PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME, |
||
435 | PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME |
||
436 | + " Persistence definition",
|
||
437 | null,
|
||
438 | null);
|
||
439 | |||
440 | // Extend the Style base definition
|
||
441 | definition.extend(manager.getDefinition(FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME)); |
||
442 | |||
443 | definition.addDynFieldBoolean(SELECTED).setMandatory(false);
|
||
444 | definition.addDynFieldObject(SELECTION_SYMBOL) |
||
445 | .setClassOfValue(PictureFillSymbol.class).setMandatory(false);
|
||
446 | definition.addDynFieldObject(BACKGROUND_IMAGE) |
||
447 | .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
|
||
448 | definition.addDynFieldObject(BACKGROUND_SELECTION_IMAGE) |
||
449 | .setClassOfValue(BackgroundFileStyle.class).setMandatory(false);
|
||
450 | definition.addDynFieldDouble(ANGLE).setMandatory(true);
|
||
451 | definition.addDynFieldDouble(X_SCALE).setMandatory(true);
|
||
452 | definition.addDynFieldDouble(Y_SCALE).setMandatory(true);
|
||
453 | definition.addDynFieldObject(MARKER_FILL_PROPERTIES) |
||
454 | .setClassOfValue(IMarkerFillPropertiesStyle.class).setMandatory(true);
|
||
455 | } |
||
456 | return Boolean.TRUE; |
||
457 | } |
||
458 | } |
||
459 | |||
460 | public static class RegisterSymbol implements Callable { |
||
461 | |||
462 | public Object call() throws Exception { |
||
463 | SymbolManager manager = MapContextLocator.getSymbolManager(); |
||
464 | |||
465 | manager.registerSymbol(PICTURE_FILL_SYMBOL_PERSISTENCE_DEFINITION_NAME, |
||
466 | PictureFillSymbol.class); |
||
467 | |||
468 | return Boolean.TRUE; |
||
469 | } |
||
470 | |||
471 | } |
||
472 | |||
473 | } |