001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-2006, GeoTools Project Managment Committee (PMC)
005: * (C) 2002, Centre for Computational Geography
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation;
010: * version 2.1 of the License.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: */
017: package org.geotools.styling;
018:
019: import org.geotools.event.AbstractGTComponent;
020: import org.geotools.factory.CommonFactoryFinder;
021: import org.geotools.factory.GeoTools;
022: import org.geotools.resources.Utilities;
023: import org.opengis.filter.FilterFactory;
024: import org.opengis.filter.expression.Expression;
025: import org.opengis.util.Cloneable;
026:
027: // J2SE dependencies
028: import java.util.ArrayList;
029: import java.util.Iterator;
030:
031: /**
032: * DOCUMENT ME!
033: *
034: * @author Ian Turton, CCG
035: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/main/src/main/java/org/geotools/styling/GraphicImpl.java $
036: * @version $Id: GraphicImpl.java 27862 2007-11-12 19:51:19Z desruisseaux $
037: */
038: public class GraphicImpl extends AbstractGTComponent implements
039: Graphic, Cloneable {
040: /** The logger for the default core module. */
041: private static final java.util.logging.Logger LOGGER = org.geotools.util.logging.Logging
042: .getLogger("org.geotools.core");
043: private FilterFactory filterFactory;
044: private String geometryPropertyName = "";
045: private java.util.List externalGraphics = new java.util.ArrayList();
046: private java.util.List marks = new java.util.ArrayList();
047: private java.util.List symbols = new java.util.ArrayList();
048: private Expression rotation = null;
049: private Expression size = null;
050: private Displacement displacement = null;
051: private Expression opacity = null;
052:
053: /**
054: * Creates a new instance of DefaultGraphic
055: */
056: protected GraphicImpl() {
057: this (CommonFactoryFinder.getFilterFactory(GeoTools
058: .getDefaultHints()));
059: }
060:
061: public GraphicImpl(FilterFactory factory) {
062: filterFactory = factory;
063: }
064:
065: public void setFilterFactory(FilterFactory factory) {
066: filterFactory = factory;
067: }
068:
069: /**
070: * Convenience method for logging a message with an exception.
071: *
072: * @param method the name of the calling method
073: * @param message the error message
074: * @param exception The exception thrown
075: */
076: private static void severe(final String method,
077: final String message, final Exception exception) {
078: final java.util.logging.LogRecord record = new java.util.logging.LogRecord(
079: java.util.logging.Level.SEVERE, message);
080: record.setSourceMethodName(method);
081: record.setThrown(exception);
082: LOGGER.log(record);
083: }
084:
085: /**
086: * Provides a list of external graphics which can be used to represent this
087: * graphic. Each one should be an equivalent representation but in a
088: * different format. If none are provided, or if none of the formats are
089: * supported, then the list of Marks should be used instead.
090: *
091: * @return An array of ExternalGraphics objects which should be equivalents
092: * but in different formats. If null is returned use getMarks
093: * instead.
094: */
095: public ExternalGraphic[] getExternalGraphics() {
096: ExternalGraphic[] ret = null;
097:
098: if (externalGraphics.size() > 0) {
099: ret = (ExternalGraphic[]) externalGraphics
100: .toArray(new ExternalGraphic[0]);
101: }
102:
103: return ret;
104: }
105:
106: public void setExternalGraphics(ExternalGraphic[] externalGraphics) {
107: this .externalGraphics.clear();
108:
109: for (int i = 0; i < symbols.size();) {
110: Object symbol = symbols.get(i);
111:
112: if (symbol instanceof ExternalGraphic) {
113: symbols.remove(i);
114: } else {
115: i++;
116: }
117: }
118:
119: if (externalGraphics != null) {
120: for (int i = 0; i < externalGraphics.length; i++) {
121: addExternalGraphic(externalGraphics[i]);
122: }
123: }
124:
125: fireChanged();
126: }
127:
128: public void addExternalGraphic(ExternalGraphic externalGraphic) {
129: externalGraphics.add(externalGraphic);
130: symbols.add(externalGraphic);
131: }
132:
133: /**
134: * Provides a list of suitable marks which can be used to represent this
135: * graphic. These should only be used if no ExternalGraphic is provided,
136: * or if none of the external graphics formats are supported.
137: *
138: * @return An array of marks to use when displaying this Graphic. By
139: * default, a "square" with 50% gray fill and black outline with a
140: * size of 6 pixels (unless a size is specified) is provided.
141: */
142: public Mark[] getMarks() {
143: Mark[] ret = new Mark[0];
144:
145: if (marks.size() > 0) {
146: ret = (Mark[]) marks.toArray(new Mark[0]);
147: }
148:
149: return ret;
150: }
151:
152: public void setMarks(Mark[] marks) {
153: this .marks.clear();
154:
155: for (int i = 0; i < symbols.size();) {
156: Object symbol = symbols.get(i);
157:
158: if (symbol instanceof Mark) {
159: symbols.remove(i);
160: } else {
161: i++;
162: }
163: }
164:
165: for (int i = 0; i < marks.length; i++) {
166: addMark(marks[i]);
167: }
168:
169: fireChanged();
170: }
171:
172: public void addMark(Mark mark) {
173: if (mark == null) {
174: return;
175: }
176:
177: marks.add(mark);
178: symbols.add(mark);
179: mark.setSize(size);
180: mark.setRotation(rotation);
181: }
182:
183: /**
184: * Provides a list of all the symbols which can be used to represent this
185: * graphic. A symbol is an ExternalGraphic, Mark or any other object which
186: * implements the Symbol interface. These are returned in the order they
187: * were set.
188: *
189: * @return An array of symbols to use when displaying this Graphic. By
190: * default, a "square" with 50% gray fill and black outline with a
191: * size of 6 pixels (unless a size is specified) is provided.
192: */
193: public Symbol[] getSymbols() {
194: Symbol[] ret = null;
195:
196: if (symbols.size() > 0) {
197: ret = (Symbol[]) symbols
198: .toArray(new Symbol[symbols.size()]);
199: } else {
200: ret = new Symbol[] { new MarkImpl() };
201: }
202:
203: return ret;
204: }
205:
206: public void setSymbols(Symbol[] symbols) {
207: this .symbols.clear();
208:
209: if (symbols != null) {
210: for (int i = 0; i < symbols.length; i++) {
211: addSymbol(symbols[i]);
212: }
213: }
214:
215: fireChanged();
216: }
217:
218: public void addSymbol(Symbol symbol) {
219: symbols.add(symbol);
220:
221: if (symbol instanceof ExternalGraphic) {
222: addExternalGraphic((ExternalGraphic) symbol);
223: }
224:
225: if (symbol instanceof Mark) {
226: addMark((Mark) symbol);
227: }
228:
229: return;
230: }
231:
232: /**
233: * This specifies the level of translucency to use when rendering the graphic.<br>
234: * The value is encoded as a floating-point value between 0.0 and 1.0 with
235: * 0.0 representing totally transparent and 1.0 representing totally
236: * opaque, with a linear scale of translucency for intermediate values.<br>
237: * For example, "0.65" would represent 65% opacity. The default value is
238: * 1.0 (opaque).
239: *
240: * @return The opacity of the Graphic, where 0.0 is completely transparent
241: * and 1.0 is completely opaque.
242: */
243: public Expression getOpacity() {
244: return opacity;
245: }
246:
247: /**
248: * This parameter defines the rotation of a graphic in the clockwise
249: * direction about its centre point in decimal degrees. The value encoded
250: * as a floating point number.
251: *
252: * @return The angle of rotation in decimal degrees. Negative values
253: * represent counter-clockwise rotation. The default is 0.0 (no
254: * rotation).
255: */
256: public Expression getRotation() {
257: return rotation;
258: }
259:
260: /**
261: * This paramteter gives the absolute size of the graphic in pixels encoded
262: * as a floating point number.
263: *
264: * <p>
265: * The default size of an image format (such as GIFD) is the inherent size
266: * of the image. The default size of a format without an inherent size
267: * (such as SVG) is defined to be 16 pixels in height and the
268: * corresponding aspect in width. If a size is specified, the height of
269: * the graphic will be scaled to that size and the corresponding aspect
270: * will be used for the width.
271: * </p>
272: *
273: * @return The size of the graphic, the default is context specific.
274: * Negative values are not possible.
275: */
276: public Expression getSize() {
277: return size;
278: }
279:
280: public Displacement getDisplacement() {
281: return displacement;
282: }
283:
284: public void setDisplacement(Displacement offset) {
285: Displacement old = this .displacement;
286: this .displacement = offset;
287: fireChildChanged("offset", offset, old);
288: }
289:
290: /**
291: * Setter for property opacity.
292: *
293: * @param opacity New value of property opacity.
294: */
295: public void setOpacity(Expression opacity) {
296: Expression old = opacity;
297: this .opacity = opacity;
298: fireChildChanged("opacity", opacity, old);
299: }
300:
301: public void setOpacity(double opacity) {
302: setOpacity(filterFactory.literal(opacity));
303: }
304:
305: /**
306: * Setter for property rotation.
307: *
308: * @param rotation New value of property rotation.
309: */
310: public void setRotation(Expression rotation) {
311: Expression old = this .rotation;
312: this .rotation = rotation;
313:
314: java.util.Iterator iter = marks.iterator();
315:
316: while (iter.hasNext()) {
317: ((MarkImpl) iter.next()).setRotation(rotation);
318: }
319:
320: fireChildChanged("rotation", rotation, old);
321: }
322:
323: public void setRotation(double rotation) {
324: setRotation(filterFactory.literal(rotation));
325: }
326:
327: /**
328: * Setter for property size.
329: *
330: * @param size New value of property size.
331: */
332: public void setSize(Expression size) {
333: Expression old = this .size;
334: this .size = size;
335:
336: java.util.Iterator iter = marks.iterator();
337:
338: while (iter.hasNext()) {
339: ((MarkImpl) iter.next()).setSize(size);
340: }
341:
342: fireChildChanged("size", size, old);
343: }
344:
345: public void setSize(int size) {
346: setSize(filterFactory.literal(size));
347: }
348:
349: public void setGeometryPropertyName(String name) {
350: geometryPropertyName = name;
351: fireChanged();
352: }
353:
354: /**
355: * Getter for property geometryPropertyName.
356: *
357: * @return Value of property geometryPropertyName.
358: */
359: public java.lang.String getGeometryPropertyName() {
360: return geometryPropertyName;
361: }
362:
363: public void accept(StyleVisitor visitor) {
364: visitor.visit(this );
365: }
366:
367: /**
368: * Creates a deep copy clone.
369: *
370: * @return The deep copy clone.
371: *
372: * @throws RuntimeException DOCUMENT ME!
373: */
374: public Object clone() {
375: GraphicImpl clone;
376:
377: try {
378: clone = (GraphicImpl) super .clone();
379: clone.marks = new ArrayList();
380: clone.externalGraphics = new ArrayList();
381: clone.symbols = new ArrayList();
382:
383: // Because ExternalGraphics and Marks are stored twice
384: // and we only want to clone them once, we should use
385: // the setter methods to place them in the proper lists
386: for (Iterator iter = externalGraphics.iterator(); iter
387: .hasNext();) {
388: ExternalGraphic exGraphic = (ExternalGraphic) iter
389: .next();
390: clone
391: .addExternalGraphic((ExternalGraphic) ((Cloneable) exGraphic)
392: .clone());
393: }
394:
395: for (Iterator iter = marks.iterator(); iter.hasNext();) {
396: Mark mark = (Mark) iter.next();
397: clone.addMark((Mark) ((Cloneable) mark).clone());
398: }
399: } catch (CloneNotSupportedException e) {
400: throw new RuntimeException(e); // this should never happen.
401: }
402:
403: return clone;
404: }
405:
406: /**
407: * Override of hashcode
408: *
409: * @return The hashcode.
410: */
411: public int hashCode() {
412: final int PRIME = 1000003;
413: int result = 0;
414:
415: if (geometryPropertyName != null) {
416: result = (PRIME * result) + geometryPropertyName.hashCode();
417: }
418:
419: if (symbols != null) {
420: result = (PRIME * result) + symbols.hashCode();
421: }
422:
423: if (rotation != null) {
424: result = (PRIME * result) + rotation.hashCode();
425: }
426:
427: if (size != null) {
428: result = (PRIME * result) + size.hashCode();
429: }
430:
431: if (opacity != null) {
432: result = (PRIME * result) + opacity.hashCode();
433: }
434:
435: return result;
436: }
437:
438: /**
439: * Compares this GraphicImpl with another for equality.
440: *
441: * <p>
442: * Two graphics are equal if and only if they both have the same geometry
443: * property name and the same list of symbols and the same rotation, size
444: * and opacity.
445: * </p>
446: *
447: * @param oth The other GraphicsImpl to compare with.
448: *
449: * @return True if this is equal to oth according to the above conditions.
450: */
451: public boolean equals(Object oth) {
452: if (this == oth) {
453: return true;
454: }
455:
456: if (oth instanceof GraphicImpl) {
457: GraphicImpl other = (GraphicImpl) oth;
458:
459: return Utilities.equals(this .geometryPropertyName,
460: other.geometryPropertyName)
461: && Utilities.equals(this .size, other.size)
462: && Utilities.equals(this .rotation, other.rotation)
463: && Utilities.equals(this .opacity, other.opacity)
464: && Utilities.equals(this .symbols, other.symbols);
465: }
466:
467: return false;
468: }
469: }
|