0001: /*
0002: * GeoTools - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2003-2006, Geotools Project Managment Committee (PMC)
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation; either
0009: * version 2.1 of the License, or (at your option) any later version.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: */
0016: package org.geotools.renderer.style;
0017:
0018: import java.awt.AlphaComposite;
0019: import java.awt.BasicStroke;
0020: import java.awt.Canvas;
0021: import java.awt.Color;
0022: import java.awt.Composite;
0023: import java.awt.FontFormatException;
0024: import java.awt.Graphics2D;
0025: import java.awt.GraphicsEnvironment;
0026: import java.awt.MediaTracker;
0027: import java.awt.Paint;
0028: import java.awt.Shape;
0029: import java.awt.Stroke;
0030: import java.awt.TexturePaint;
0031: import java.awt.geom.AffineTransform;
0032: import java.awt.geom.Point2D;
0033: import java.awt.geom.Rectangle2D;
0034: import java.awt.image.AffineTransformOp;
0035: import java.awt.image.BufferedImage;
0036: import java.io.File;
0037: import java.io.FileInputStream;
0038: import java.io.FileNotFoundException;
0039: import java.io.IOException;
0040: import java.io.InputStream;
0041: import java.net.MalformedURLException;
0042: import java.net.URL;
0043: import java.util.ArrayList;
0044: import java.util.Arrays;
0045: import java.util.HashMap;
0046: import java.util.HashSet;
0047: import java.util.Iterator;
0048: import java.util.List;
0049: import java.util.Map;
0050: import java.util.Set;
0051: import java.util.WeakHashMap;
0052: import java.util.logging.Level;
0053: import java.util.logging.Logger;
0054:
0055: import javax.imageio.ImageIO;
0056: import javax.media.jai.util.Range;
0057:
0058: import org.apache.batik.transcoder.SVGAbstractTranscoder;
0059: import org.apache.batik.transcoder.TranscoderInput;
0060: import org.apache.batik.transcoder.TranscoderOutput;
0061: import org.geotools.factory.CommonFactoryFinder;
0062: import org.geotools.feature.Feature;
0063: import org.opengis.filter.FilterFactory;
0064: import org.opengis.filter.expression.Expression;
0065: import org.opengis.filter.expression.PropertyName;
0066: import org.geotools.renderer.lite.CustomGlyphRenderer;
0067: import org.geotools.renderer.lite.GlyphRenderer;
0068: import org.geotools.renderer.lite.SVGGlyphRenderer;
0069: import org.geotools.styling.ExternalGraphic;
0070: import org.geotools.styling.Fill;
0071: import org.geotools.styling.Font;
0072: import org.geotools.styling.Graphic;
0073: import org.geotools.styling.Halo;
0074: import org.geotools.styling.LabelPlacement;
0075: import org.geotools.styling.LinePlacement;
0076: import org.geotools.styling.LineSymbolizer;
0077: import org.geotools.styling.Mark;
0078: import org.geotools.styling.PointPlacement;
0079: import org.geotools.styling.PointSymbolizer;
0080: import org.geotools.styling.PolygonSymbolizer;
0081: import org.geotools.styling.StyleAttributeExtractorTruncated;
0082: import org.geotools.styling.StyleFactoryFinder;
0083: import org.geotools.styling.Symbol;
0084: import org.geotools.styling.Symbolizer;
0085: import org.geotools.styling.TextMark;
0086: import org.geotools.styling.TextSymbolizer;
0087: import org.geotools.styling.TextSymbolizer2;
0088: import org.geotools.util.SoftValueHashMap;
0089: import org.w3c.dom.Document;
0090:
0091: import com.vividsolutions.jts.geom.Geometry;
0092:
0093: /**
0094: * Factory object that converts SLD style into rendered styles.
0095: *
0096: * DJB: I've made a few changes to this.
0097: * The old behavior was for this class to convert <LinePlacement> tags to <PointPlacement> tags.
0098: * (ie. there never was a LinePlacement option)
0099: * This is *certainly* not the correct place to do this, and it was doing a very poor job of it too,
0100: * and the renderer was not expecting it to be doing it!
0101: *
0102: * I added support in TextStyle3D for this and had this class correctly set Line/Point placement selection.
0103: * NOTE: PointPlacement is the default if not present.
0104: *
0105: * @author aaime
0106: * @author dblasby
0107: */
0108:
0109: /*
0110: * orginal message on the subject:
0111: *
0112: * I was attempting to write documentation for label placement (plus fix
0113: all the inconsistencies with the spec), and I noticed some problems
0114: with the SLDStyleFactory and TextStyle2D.
0115:
0116: It turns out the SLDStyleFactory is actually trying to do [poor] label
0117: placement (see around line 570)! This also results in a loss of
0118: information if you're using a <LinePlacement> element in your SLD.
0119:
0120:
0121: 1. remove the placement code from SLDStyleFactory!
0122: 2. get rid of the "AbsoluteLineDisplacement" stuff and replace it with
0123: something that represents <PointPlacement>/<LinePlacement> elements in
0124: the TextSymbolizer.
0125:
0126: The current implementation seems to try to convert a <LinePlacement> and
0127: an actual line into a <PointPlacement> (and setting the
0128: AbsoluteLineDisplacement flag)!! This should be done by the real
0129: labeling code.
0130:
0131: This change could affect the j2d renderer as it appears to use the
0132: "AbsoluteLineDisplacement" flag.
0133: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/render/src/main/java/org/geotools/renderer/style/SLDStyleFactory.java $
0134: */
0135:
0136: public class SLDStyleFactory {
0137: /** The logger for the rendering module. */
0138: private static final Logger LOGGER = org.geotools.util.logging.Logging
0139: .getLogger("org.geotools.rendering");
0140:
0141: /** Holds a lookup bewteen SLD names and java constants. */
0142: private static final java.util.Map joinLookup = new java.util.HashMap();
0143:
0144: /** Holds a lookup bewteen SLD names and java constants. */
0145: private static final java.util.Map capLookup = new java.util.HashMap();
0146:
0147: /** Holds a lookup bewteen SLD names and java constants. */
0148: private static final java.util.Map fontStyleLookup = new java.util.HashMap();
0149:
0150: private static final FilterFactory ff = CommonFactoryFinder
0151: .getFilterFactory(null);
0152:
0153: /** Set containing the font families known of this machine */
0154: private static Set fontFamilies = null;
0155:
0156: /** Fonts already loaded */
0157: private static Map loadedFonts = new HashMap();
0158:
0159: /** Holds the set of well-known marks. */
0160: static Set wellKnownMarks = new java.util.HashSet();
0161:
0162: /** Holds the of graphic formats supported by the current jdk */
0163: static Set supportedGraphicFormats = null;
0164:
0165: /** Current way to load images */
0166: static ImageLoader imageLoader = new ImageLoader();
0167:
0168: /** This one is used as the observer object in image tracks */
0169: private static final Canvas obs = new Canvas();
0170:
0171: /** This one holds the list of glyphRenderers that can convert glyphs into an image */
0172: private static List glyphRenderers = new ArrayList();
0173:
0174: static { //static block to populate the lookups
0175: joinLookup.put("miter", new Integer(BasicStroke.JOIN_MITER));
0176: joinLookup.put("bevel", new Integer(BasicStroke.JOIN_BEVEL));
0177: joinLookup.put("round", new Integer(BasicStroke.JOIN_ROUND));
0178:
0179: capLookup.put("butt", new Integer(BasicStroke.CAP_BUTT));
0180: capLookup.put("round", new Integer(BasicStroke.CAP_ROUND));
0181: capLookup.put("square", new Integer(BasicStroke.CAP_SQUARE));
0182:
0183: fontStyleLookup.put("normal", new Integer(java.awt.Font.PLAIN));
0184: fontStyleLookup
0185: .put("italic", new Integer(java.awt.Font.ITALIC));
0186: fontStyleLookup.put("oblique",
0187: new Integer(java.awt.Font.ITALIC));
0188: fontStyleLookup.put("bold", new Integer(java.awt.Font.BOLD));
0189:
0190: /**
0191: * A list of wellknownshapes that we know about: square, circle, triangle, star, cross, x.
0192: * Note arrow is an implementation specific mark.
0193: */
0194: wellKnownMarks.add("Square");
0195: wellKnownMarks.add("Triangle");
0196: wellKnownMarks.add("Cross");
0197: wellKnownMarks.add("Circle");
0198: wellKnownMarks.add("Star");
0199: wellKnownMarks.add("X");
0200: wellKnownMarks.add("Arrow");
0201: wellKnownMarks.add("Hatch");
0202: wellKnownMarks.add("square");
0203: wellKnownMarks.add("triangle");
0204: wellKnownMarks.add("cross");
0205: wellKnownMarks.add("circle");
0206: wellKnownMarks.add("star");
0207: wellKnownMarks.add("x");
0208: wellKnownMarks.add("arrow");
0209: wellKnownMarks.add("hatch");
0210:
0211: /**
0212: * Initialize the gliph renderers array with the default ones
0213: */
0214: glyphRenderers.add(new CustomGlyphRenderer());
0215:
0216: try {
0217: glyphRenderers.add(new SVGGlyphRenderer());
0218: } catch (Exception e) {
0219: LOGGER.warning("Will not support SVG External Graphics "
0220: + e);
0221: }
0222: }
0223:
0224: /** Parsed SVG glyphs */
0225: Map svgGlyphs = new SoftValueHashMap();
0226:
0227: /** Symbolizers that depend on attributes */
0228: Map dynamicSymbolizers = new SoftValueHashMap();
0229:
0230: /** Symbolizers that do not depend on attributes */
0231: Map staticSymbolizers = new SoftValueHashMap();
0232:
0233: private static Set getSupportedGraphicFormats() {
0234: if (supportedGraphicFormats == null) {
0235: supportedGraphicFormats = new java.util.HashSet();
0236:
0237: String[] types = ImageIO.getReaderMIMETypes();
0238:
0239: for (int i = 0; i < types.length; i++) {
0240: supportedGraphicFormats.add(types[i]);
0241: }
0242: }
0243:
0244: return supportedGraphicFormats;
0245: }
0246:
0247: private long hits;
0248:
0249: private long requests;
0250:
0251: /**
0252: * Holds value of property mapScaleDenominator.
0253: */
0254: private double mapScaleDenominator = Double.NaN;;
0255:
0256: public double getHitRatio() {
0257: return (double) hits / (double) requests;
0258: }
0259:
0260: public long getHits() {
0261: return hits;
0262: }
0263:
0264: public long getRequests() {
0265: return requests;
0266: }
0267:
0268: /**
0269: * <p>
0270: * Creates a rendered style
0271: * </p>
0272: *
0273: * <p>
0274: * Makes use of a symbolizer cache based on identity to avoid recomputing over and over the
0275: * same style object and to reduce memory usage. The same Style2D object will be returned by
0276: * subsequent calls using the same feature independent symbolizer with the same scaleRange.
0277: * </p>
0278: *
0279: * @param drawMe The feature
0280: * @param symbolizer The SLD symbolizer
0281: * @param scaleRange The scale range in which the feature should be painted according to the
0282: * symbolizer
0283: *
0284: * @return A rendered style equivalent to the symbolizer
0285: */
0286: public Style2D createStyle(Object drawMe, Symbolizer symbolizer,
0287: Range scaleRange) {
0288: Style2D style = null;
0289:
0290: SymbolizerKey key = new SymbolizerKey(symbolizer, scaleRange);
0291: style = (Style2D) staticSymbolizers.get(key);
0292:
0293: requests++;
0294:
0295: if (style != null) {
0296: hits++;
0297: } else {
0298: style = createStyleInternal(drawMe, symbolizer, scaleRange);
0299:
0300: // if known dynamic symbolizer return the style
0301: if (dynamicSymbolizers.containsKey(key)) {
0302: return style;
0303: } else {
0304: // lets see if it's static or dynamic
0305: StyleAttributeExtractorTruncated sae = new StyleAttributeExtractorTruncated();
0306: sae.visit(symbolizer);
0307:
0308: Set nameSet = sae.getAttributeNameSet();
0309:
0310: if ((nameSet == null) || (nameSet.size() == 0)) {
0311: staticSymbolizers.put(key, style);
0312: } else {
0313: dynamicSymbolizers.put(key, Boolean.TRUE);
0314: }
0315: }
0316: }
0317: return style;
0318: }
0319:
0320: /**
0321: * Really creates the symbolizer
0322: *
0323: * @param drawMe DOCUMENT ME!
0324: * @param symbolizer DOCUMENT ME!
0325: * @param scaleRange DOCUMENT ME!
0326: *
0327: * @return DOCUMENT ME!
0328: */
0329: private Style2D createStyleInternal(Object drawMe,
0330: Symbolizer symbolizer, Range scaleRange) {
0331: Style2D style = null;
0332:
0333: if (symbolizer instanceof PolygonSymbolizer) {
0334: style = createPolygonStyle(drawMe,
0335: (PolygonSymbolizer) symbolizer, scaleRange);
0336: } else if (symbolizer instanceof LineSymbolizer) {
0337: style = createLineStyle(drawMe,
0338: (LineSymbolizer) symbolizer, scaleRange);
0339: } else if (symbolizer instanceof PointSymbolizer) {
0340: style = createPointStyle(drawMe,
0341: (PointSymbolizer) symbolizer, scaleRange);
0342: } else if (symbolizer instanceof TextSymbolizer) {
0343: style = createTextStyle(drawMe,
0344: (TextSymbolizer) symbolizer, scaleRange);
0345: }
0346:
0347: return style;
0348: }
0349:
0350: /**
0351: * Creates a rendered style
0352: *
0353: * @param f The feature
0354: * @param symbolizer The SLD symbolizer
0355: * @param scaleRange The scale range in which the feature should be painted according to the
0356: * symbolizer
0357: *
0358: * @return A rendered style equivalent to the symbolizer
0359: *
0360: * @throws UnsupportedOperationException if an unknown symbolizer is passed to this method
0361: */
0362: public Style2D createDynamicStyle(Feature f, Symbolizer symbolizer,
0363: Range scaleRange) {
0364: Style2D style = null;
0365:
0366: if (symbolizer instanceof PolygonSymbolizer) {
0367: style = createDynamicPolygonStyle(f,
0368: (PolygonSymbolizer) symbolizer, scaleRange);
0369: } else if (symbolizer instanceof LineSymbolizer) {
0370: style = createDynamicLineStyle(f,
0371: (LineSymbolizer) symbolizer, scaleRange);
0372: } else {
0373: throw new UnsupportedOperationException(
0374: "This kind of symbolizer is not yet supported");
0375: }
0376:
0377: return style;
0378: }
0379:
0380: Style2D createPolygonStyle(Object feature,
0381: PolygonSymbolizer symbolizer, Range scaleRange) {
0382: PolygonStyle2D style = new PolygonStyle2D();
0383:
0384: setScaleRange(style, scaleRange);
0385: style.setStroke(getStroke(symbolizer.getStroke(), feature));
0386: style.setGraphicStroke(getGraphicStroke(symbolizer.getStroke(),
0387: feature));
0388: style
0389: .setContour(getStrokePaint(symbolizer.getStroke(),
0390: feature));
0391: style.setContourComposite(getStrokeComposite(symbolizer
0392: .getStroke(), feature));
0393: style.setFill(getPaint(symbolizer.getFill(), feature));
0394: style.setFillComposite(getComposite(symbolizer.getFill(),
0395: feature));
0396:
0397: return style;
0398: }
0399:
0400: Style2D createDynamicPolygonStyle(Feature feature,
0401: PolygonSymbolizer symbolizer, Range scaleRange) {
0402: PolygonStyle2D style = new DynamicPolygonStyle2D(feature,
0403: symbolizer);
0404:
0405: setScaleRange(style, scaleRange);
0406:
0407: //setStroke(style, symbolizer.getStroke(), feature);
0408: //setFill(style, symbolizer.getFill(), feature);
0409: return style;
0410: }
0411:
0412: Style2D createLineStyle(Object feature, LineSymbolizer symbolizer,
0413: Range scaleRange) {
0414: LineStyle2D style = new LineStyle2D();
0415: setScaleRange(style, scaleRange);
0416: style.setStroke(getStroke(symbolizer.getStroke(), feature));
0417: style.setGraphicStroke(getGraphicStroke(symbolizer.getStroke(),
0418: feature));
0419: style
0420: .setContour(getStrokePaint(symbolizer.getStroke(),
0421: feature));
0422: style.setContourComposite(getStrokeComposite(symbolizer
0423: .getStroke(), feature));
0424:
0425: return style;
0426: }
0427:
0428: Style2D createDynamicLineStyle(Feature feature,
0429: LineSymbolizer symbolizer, Range scaleRange) {
0430: LineStyle2D style = new DynamicLineStyle2D(feature, symbolizer);
0431: setScaleRange(style, scaleRange);
0432:
0433: //setStroke(style, symbolizer.getStroke(), feature);
0434: return style;
0435: }
0436:
0437: Style2D createPointStyle(Object feature,
0438: PointSymbolizer symbolizer, Range scaleRange) {
0439: Style2D retval = null;
0440:
0441: // extract base properties
0442: Graphic sldGraphic = symbolizer.getGraphic();
0443: float opacity = evalOpacity(sldGraphic.getOpacity(), feature);
0444: int size;
0445:
0446: try {
0447: size = (int) evalToDouble(sldGraphic.getSize(), feature, 10);
0448: } catch (NumberFormatException nfe) {
0449: size = 10;
0450: }
0451:
0452: float rotation = (float) ((evalToFloat(
0453: sldGraphic.getRotation(), feature, 0) * Math.PI) / 180);
0454:
0455: // Extract the sequence of external graphics and symbols and process them in order
0456: // to recognize which one will be used for rendering
0457: Symbol[] symbols = sldGraphic.getSymbols();
0458: final int length = symbols.length;
0459: ExternalGraphic eg;
0460: GlyphRenderer r;
0461: BufferedImage img = null;
0462: double dsize;
0463: AffineTransform scaleTx;
0464: AffineTransformOp ato;
0465: BufferedImage scaledImage;
0466: Mark mark;
0467: Shape shape;
0468: MarkStyle2D ms2d;
0469: for (int i = 0; i < length; i++) {
0470: if (LOGGER.isLoggable(Level.FINER)) {
0471: LOGGER.finer("trying to render symbol " + i);
0472: }
0473:
0474: // try loading external graphic and creating a GraphicsStyle2D
0475: if (symbols[i] instanceof ExternalGraphic) {
0476: if (LOGGER.isLoggable(Level.FINER)) {
0477: LOGGER.finer("rendering External graphic");
0478: }
0479:
0480: eg = (ExternalGraphic) symbols[i];
0481: img = null;
0482:
0483: // first see if any glyph renderers can handle this
0484: for (Iterator it = glyphRenderers.iterator(); it
0485: .hasNext()
0486: && (img == null);) {
0487: r = (GlyphRenderer) it.next();
0488:
0489: if (r.canRender(eg.getFormat())) {
0490: img = r.render(sldGraphic, eg, feature, size);
0491: break; // dont render twice
0492: }
0493: }
0494:
0495: // if no-one of the glyph renderers can handle the eg, try to load it as an external image
0496: if (img == null) {
0497: img = getImage(eg, size); //size is only a hint
0498: }
0499:
0500: if (img == null) {
0501: continue;
0502: }
0503:
0504: //SLD SPEC page 52 on ExternalGraphic
0505: // The default size of an image format (such as GIF) is the inherent size of the image. The
0506: //default size of a format without an inherent size (such as SVG) is defined to be 16 pixels
0507: //in height and the corresponding aspect in width. If a size is specified, the height of the
0508: //graphic will be scaled to that size and the corresponding aspect will be used for the width.
0509:
0510: // dsize = (double) size;
0511: // scaleTx = AffineTransform.getScaleInstance(dsize
0512: // / img.getWidth(), dsize / img.getHeight());
0513: // ato = new AffineTransformOp(scaleTx,
0514: // AffineTransformOp.TYPE_BILINEAR);
0515: // scaledImage = ato.createCompatibleDestImage(img, img
0516: // .getColorModel());
0517: // img = ato.filter(img, scaledImage);
0518:
0519: // therefore, we determine the scaling required for "y" (height), then calculate a corresponding
0520: // scaling for "x" in which we keep the original's aspect ratio
0521:
0522: if (img.getHeight() != size) //need to scale
0523: {
0524: dsize = (double) size;
0525:
0526: double scaleY = dsize / img.getHeight(); // >1 if you're magnifying
0527: double scaleX = scaleY; // keep aspect ratio!
0528:
0529: scaleTx = AffineTransform.getScaleInstance(scaleX,
0530: scaleY); //DJB: keep aspect ratio
0531: ato = new AffineTransformOp(scaleTx,
0532: AffineTransformOp.TYPE_BILINEAR);
0533: scaledImage = ato.createCompatibleDestImage(img,
0534: img.getColorModel());
0535: img = ato.filter(img, scaledImage);
0536: }
0537:
0538: if (img != null) {
0539: retval = new GraphicStyle2D(img, rotation, opacity);
0540:
0541: break;
0542: }
0543: }
0544:
0545: if (symbols[i] instanceof Mark) {
0546: if (LOGGER.isLoggable(Level.FINER)) {
0547: LOGGER.finer("rendering mark @ PointRenderer "
0548: + symbols[i].toString());
0549: }
0550:
0551: mark = (Mark) symbols[i];
0552: shape = Java2DMark.getWellKnownMark(mark
0553: .getWellKnownName().evaluate(feature)
0554: .toString());
0555:
0556: ms2d = new MarkStyle2D();
0557: ms2d.setShape(shape);
0558: ms2d.setFill(getPaint(mark.getFill(), feature));
0559: ms2d.setFillComposite(getComposite(mark.getFill(),
0560: feature));
0561: ms2d.setStroke(getStroke(mark.getStroke(), feature));
0562: ms2d.setContour(getStrokePaint(mark.getStroke(),
0563: feature));
0564: ms2d.setContourComposite(getStrokeComposite(mark
0565: .getStroke(), feature));
0566: ms2d.setSize(size);
0567: ms2d.setRotation(rotation);
0568: retval = ms2d;
0569:
0570: break;
0571: }
0572:
0573: if (symbols[i] instanceof TextMark) {
0574: // for the moment don't support TextMarks since they are not part
0575: // of the SLD specification
0576: continue;
0577:
0578: /**
0579: * if (LOGGER.isLoggable(Level.FINER)) { LOGGER.finer("rendering text symbol");
0580: * } flag = renderTextSymbol(geom, sldgraphic, feature, (TextMark) symbols[i]); if
0581: * (flag) { return; }
0582: */
0583: }
0584: }
0585:
0586: if (retval != null) {
0587: setScaleRange(retval, scaleRange);
0588: }
0589:
0590: return retval;
0591: }
0592:
0593: Style2D createTextStyle(Object feature, TextSymbolizer symbolizer,
0594: Range scaleRange) {
0595: TextStyle2D ts2d = new TextStyle2D();
0596: setScaleRange(ts2d, scaleRange);
0597:
0598: if (LOGGER.isLoggable(Level.FINER)) {
0599: LOGGER.finer("creating text style");
0600: }
0601:
0602: String geomName = symbolizer.getGeometryPropertyName();
0603:
0604: if (LOGGER.isLoggable(Level.FINER)) {
0605: LOGGER.finer("geomName = " + geomName);
0606: }
0607:
0608: // extract label
0609: Object obj = symbolizer.getLabel().evaluate(feature);
0610: String label = obj == null ? "" : obj.toString();
0611:
0612: if (LOGGER.isLoggable(Level.FINER)) {
0613: LOGGER.finer("label is " + label);
0614: }
0615:
0616: ts2d.setLabel(label);
0617:
0618: // get the sequence of fonts to be used and set the first one available
0619: Font[] fonts = symbolizer.getFonts();
0620: java.awt.Font javaFont = getFont(feature, fonts);
0621: ts2d.setFont(javaFont);
0622:
0623: // compute label position, anchor, rotation and displacement
0624: LabelPlacement placement = symbolizer.getLabelPlacement();
0625: double anchorX = 0;
0626: double anchorY = 0;
0627: double rotation = 0;
0628: double dispX = 0;
0629: double dispY = 0;
0630:
0631: if (placement instanceof PointPlacement) {
0632: if (LOGGER.isLoggable(Level.FINER)) {
0633: LOGGER.finer("setting pointPlacement");
0634: }
0635:
0636: // compute anchor point and displacement
0637: PointPlacement p = (PointPlacement) placement;
0638: anchorX = ((Number) p.getAnchorPoint().getAnchorPointX()
0639: .evaluate(feature)).doubleValue();
0640: anchorY = ((Number) p.getAnchorPoint().getAnchorPointY()
0641: .evaluate(feature)).doubleValue();
0642:
0643: dispX = ((Number) p.getDisplacement().getDisplacementX()
0644: .evaluate(feature)).doubleValue();
0645: dispY = ((Number) p.getDisplacement().getDisplacementY()
0646: .evaluate(feature)).doubleValue();
0647:
0648: // rotation
0649: if ((symbolizer instanceof TextSymbolizer2)
0650: && (((TextSymbolizer2) symbolizer).getGraphic() != null)) {
0651: // don't rotate labels that are being placed on shields.
0652: rotation = 0.0;
0653: } else {
0654: rotation = ((Number) p.getRotation().evaluate(feature))
0655: .doubleValue();
0656: rotation *= (Math.PI / 180.0);
0657: }
0658:
0659: ts2d.setPointPlacement(true);
0660: } else if (placement instanceof LinePlacement) {
0661: // this code used to really really really really suck, so I removed it!
0662: if (LOGGER.isLoggable(Level.FINER)) {
0663: LOGGER.finer("setting pointPlacement");
0664: }
0665: ts2d.setPointPlacement(false);
0666: LinePlacement p = (LinePlacement) placement;
0667: int displace = ((Number) p.getPerpendicularOffset()
0668: .evaluate(feature)).intValue();
0669: ts2d.setPerpendicularOffset(displace);
0670: }
0671:
0672: ts2d.setAnchorX(anchorX);
0673: ts2d.setAnchorY(anchorY);
0674: ts2d.setRotation((float) rotation);
0675: ts2d.setDisplacementX(dispX);
0676: ts2d.setDisplacementY(dispY);
0677:
0678: // setup fill and composite
0679: ts2d.setFill(getPaint(symbolizer.getFill(), feature));
0680: ts2d.setComposite(getComposite(symbolizer.getFill(), feature));
0681:
0682: // compute halo parameters
0683: Halo halo = symbolizer.getHalo();
0684:
0685: if (halo != null) {
0686: ts2d.setHaloFill(getPaint(halo.getFill(), feature));
0687: ts2d
0688: .setHaloComposite(getComposite(halo.getFill(),
0689: feature));
0690: ts2d.setHaloRadius(((Number) halo.getRadius().evaluate(
0691: feature)).floatValue());
0692: }
0693:
0694: Graphic graphicShield = null;
0695: if (symbolizer instanceof TextSymbolizer2) {
0696: graphicShield = ((TextSymbolizer2) symbolizer).getGraphic();
0697: if (graphicShield != null) {
0698: PointSymbolizer p = StyleFactoryFinder
0699: .createStyleFactory().createPointSymbolizer();
0700: p.setGraphic(graphicShield);
0701:
0702: Style2D shieldStyle = createPointStyle(feature, p,
0703: scaleRange);
0704: ts2d.setGraphic(shieldStyle);
0705: }
0706: }
0707:
0708: return ts2d;
0709: }
0710:
0711: /**
0712: * Extracts the named geometry from feature. If geomName is null then the feature's default
0713: * geometry is used. If geomName cannot be found in feature then null is returned.
0714: *
0715: * @param feature The feature to find the geometry in
0716: * @param geomName The name of the geometry to find: null if the default geometry should be
0717: * used.
0718: *
0719: * @return The geometry extracted from feature or null if this proved impossible.
0720: */
0721: private Geometry findGeometry(final Object feature, String geomName) {
0722: Geometry geom = null;
0723:
0724: if (geomName == null) {
0725: geomName = ""; // ie default geometry
0726: }
0727: PropertyName property = ff.property(geomName);
0728: return (Geometry) property.evaluate(feature, Geometry.class);
0729: }
0730:
0731: /**
0732: * Returns the first font associated to the feature that can be found on the current machine
0733: *
0734: * @param feature The feature whose font is to be found
0735: * @param fonts An array of fonts dependent of the feature, the first that is found on the
0736: * current machine is returned
0737: *
0738: * @return The first of the specified fonts found on this machine or null if none found
0739: */
0740: private java.awt.Font getFont(Object feature, Font[] fonts) {
0741: // we need to synchronize font loading or multithreaded access may
0742: // result in us returning the wrong font
0743: synchronized (loadedFonts) {
0744: if (fontFamilies == null) {
0745: GraphicsEnvironment ge = GraphicsEnvironment
0746: .getLocalGraphicsEnvironment();
0747: fontFamilies = new HashSet();
0748:
0749: List f = Arrays
0750: .asList(ge.getAvailableFontFamilyNames());
0751: fontFamilies.addAll(f);
0752:
0753: if (LOGGER.isLoggable(Level.FINEST)) {
0754: LOGGER.finest("there are " + fontFamilies.size()
0755: + " fonts available");
0756: }
0757: }
0758:
0759: java.awt.Font javaFont = null;
0760:
0761: int styleCode = 0;
0762: int size = 6;
0763: String requestedFont = "";
0764:
0765: for (int k = 0; k < fonts.length; k++) {
0766: requestedFont = fonts[k].getFontFamily().evaluate(
0767: feature).toString();
0768:
0769: if (LOGGER.isLoggable(Level.FINEST)) {
0770: LOGGER.finest("trying to load " + requestedFont);
0771: }
0772:
0773: if (loadedFonts.containsKey(requestedFont)) {
0774: javaFont = (java.awt.Font) loadedFonts
0775: .get(requestedFont);
0776:
0777: String reqStyle = (String) fonts[k].getFontStyle()
0778: .evaluate(feature);
0779:
0780: if (fontStyleLookup.containsKey(reqStyle)) {
0781: styleCode = ((Integer) fontStyleLookup
0782: .get(reqStyle)).intValue();
0783: } else {
0784: styleCode = java.awt.Font.PLAIN;
0785: }
0786:
0787: String reqWeight = (String) fonts[k]
0788: .getFontWeight().evaluate(feature);
0789:
0790: if (reqWeight.equalsIgnoreCase("Bold")) {
0791: styleCode = styleCode | java.awt.Font.BOLD;
0792: }
0793:
0794: size = ((Number) fonts[k].getFontSize().evaluate(
0795: feature)).intValue();
0796:
0797: return javaFont.deriveFont(styleCode, size);
0798: }
0799:
0800: if (LOGGER.isLoggable(Level.FINEST)) {
0801: LOGGER.finest("not already loaded");
0802: }
0803:
0804: if (fontFamilies.contains(requestedFont)) {
0805: String reqStyle = (String) fonts[k].getFontStyle()
0806: .evaluate(feature);
0807:
0808: if (fontStyleLookup.containsKey(reqStyle)) {
0809: styleCode = ((Integer) fontStyleLookup
0810: .get(reqStyle)).intValue();
0811: } else {
0812: styleCode = java.awt.Font.PLAIN;
0813: }
0814:
0815: String reqWeight = (String) fonts[k]
0816: .getFontWeight().evaluate(feature);
0817:
0818: if (reqWeight.equalsIgnoreCase("Bold")) {
0819: styleCode = styleCode | java.awt.Font.BOLD;
0820: }
0821:
0822: size = ((Number) fonts[k].getFontSize().evaluate(
0823: feature)).intValue();
0824:
0825: if (LOGGER.isLoggable(Level.FINEST)) {
0826: LOGGER.finest("requesting " + requestedFont
0827: + " " + styleCode + " " + size);
0828: }
0829:
0830: javaFont = new java.awt.Font(requestedFont,
0831: styleCode, size);
0832: loadedFonts.put(requestedFont, javaFont);
0833:
0834: return javaFont;
0835: }
0836:
0837: if (LOGGER.isLoggable(Level.FINEST)) {
0838: LOGGER.finest("not a system font");
0839: }
0840:
0841: // may be its a file or url
0842: InputStream is = null;
0843:
0844: if (requestedFont.startsWith("http")
0845: || requestedFont.startsWith("file:")) {
0846: try {
0847: URL url = new URL(requestedFont);
0848: is = url.openStream();
0849: } catch (MalformedURLException mue) {
0850: // this may be ok - but we should mention it
0851: if (LOGGER.isLoggable(Level.INFO)) {
0852: LOGGER.info("Bad url in SLDStyleFactory "
0853: + requestedFont + "\n" + mue);
0854: }
0855: } catch (IOException ioe) {
0856: // we'll ignore this for the moment
0857: if (LOGGER.isLoggable(Level.INFO)) {
0858: LOGGER.info("IO error in SLDStyleFactory "
0859: + requestedFont + "\n" + ioe);
0860: }
0861: }
0862: } else {
0863: if (LOGGER.isLoggable(Level.FINEST)) {
0864: LOGGER.finest("not a URL");
0865: }
0866:
0867: File file = new File(requestedFont);
0868:
0869: //if(file.canRead()){
0870: try {
0871: is = new FileInputStream(file);
0872: } catch (FileNotFoundException fne) {
0873: // this may be ok - but we should mention it
0874: if (LOGGER.isLoggable(Level.INFO)) {
0875: LOGGER
0876: .info("Bad file name in SLDStyleFactory"
0877: + requestedFont
0878: + "\n"
0879: + fne);
0880: }
0881: }
0882: }
0883:
0884: if (LOGGER.isLoggable(Level.FINEST)) {
0885: LOGGER.finest("about to load");
0886: }
0887:
0888: if (is == null) {
0889: if (LOGGER.isLoggable(Level.INFO)) {
0890: LOGGER.info("null input stream");
0891: }
0892:
0893: continue;
0894: }
0895:
0896: try {
0897: javaFont = java.awt.Font.createFont(
0898: java.awt.Font.TRUETYPE_FONT, is);
0899: } catch (FontFormatException ffe) {
0900: if (LOGGER.isLoggable(Level.INFO)) {
0901: LOGGER
0902: .info("Font format error in SLDStyleFactory "
0903: + requestedFont + "\n" + ffe);
0904: }
0905:
0906: continue;
0907: } catch (IOException ioe) {
0908: // we'll ignore this for the moment
0909: if (LOGGER.isLoggable(Level.INFO)) {
0910: LOGGER.info("IO error in SLDStyleFactory "
0911: + requestedFont + "\n" + ioe);
0912: }
0913:
0914: continue;
0915: }
0916:
0917: loadedFonts.put(requestedFont, javaFont);
0918:
0919: return javaFont;
0920: }
0921:
0922: // if everything else fails fall back on a default font distributed
0923: // along with the jdk
0924: return new java.awt.Font("Serif", java.awt.Font.PLAIN, 12);
0925: }
0926: }
0927:
0928: void setScaleRange(Style style, Range scaleRange) {
0929: double min = ((Number) scaleRange.getMinValue()).doubleValue();
0930: double max = ((Number) scaleRange.getMaxValue()).doubleValue();
0931: style.setMinMaxScale(min, max);
0932: }
0933:
0934: // Builds an image version of the graphics with the proper size, no further scaling will
0935: // be needed during rendering
0936: private BufferedImage getGraphicStroke(
0937: org.geotools.styling.Stroke stroke, Object feature) {
0938: if ((stroke == null) || (stroke.getGraphicStroke() == null)) {
0939: return null;
0940: }
0941:
0942: Graphic graphicStroke = stroke.getGraphicStroke();
0943:
0944: // lets see if an external image is to be used
0945: BufferedImage image = getExternalGraphic(graphicStroke);
0946:
0947: double size = ((Number) graphicStroke.getSize().evaluate(
0948: feature)).doubleValue();
0949:
0950: if (image != null) {
0951: int trueImageWidth = image.getWidth();
0952: int trueImageHeight = image.getHeight();
0953: double scalex = size / trueImageWidth;
0954: double scaley = size / trueImageWidth;
0955:
0956: AffineTransform at = AffineTransform.getScaleInstance(
0957: scalex, scaley);
0958: AffineTransformOp ato = new AffineTransformOp(at,
0959: AffineTransformOp.TYPE_BILINEAR);
0960: BufferedImage scaledImage = ato.createCompatibleDestImage(
0961: image, image.getColorModel());
0962: ato.filter(image, scaledImage);
0963:
0964: image = scaledImage;
0965:
0966: if (LOGGER.isLoggable(Level.FINER)) {
0967: LOGGER.finer("got an image in graphic fill");
0968: }
0969: } else {
0970: if (LOGGER.isLoggable(Level.FINER)) {
0971: LOGGER.finer("going for the mark from graphic fill");
0972: }
0973:
0974: Mark mark = getMark(graphicStroke, feature);
0975: image = new BufferedImage((int) size, (int) size,
0976: BufferedImage.TYPE_INT_ARGB);
0977:
0978: Graphics2D ig2d = image.createGraphics();
0979: double rotation = 0.0;
0980: rotation = ((Number) graphicStroke.getRotation().evaluate(
0981: feature)).doubleValue();
0982: rotation *= (Math.PI / 180.0);
0983: fillDrawMark(ig2d, size / 2, size / 2, mark, (int) size,
0984: rotation, feature);
0985:
0986: MediaTracker track = new MediaTracker(obs);
0987: track.addImage(image, 1);
0988:
0989: try {
0990: track.waitForID(1);
0991: } catch (InterruptedException e) {
0992: LOGGER.warning(e.toString());
0993: }
0994: }
0995:
0996: return image;
0997: }
0998:
0999: private Stroke getStroke(org.geotools.styling.Stroke stroke,
1000: Object feature) {
1001: if (stroke == null) {
1002: return null;
1003: }
1004:
1005: // resolve join type into a join code
1006: String joinType;
1007: int joinCode;
1008:
1009: joinType = evaluateExpression(stroke.getLineJoin(), feature,
1010: "miter");
1011:
1012: if (joinLookup.containsKey(joinType)) {
1013: joinCode = ((Integer) joinLookup.get(joinType)).intValue();
1014: } else {
1015: joinCode = java.awt.BasicStroke.JOIN_MITER;
1016: }
1017:
1018: // resolve cap type into a cap code
1019: String capType;
1020: int capCode;
1021:
1022: capType = evaluateExpression(stroke.getLineCap(), feature,
1023: "square");
1024:
1025: if (capLookup.containsKey(capType)) {
1026: capCode = ((Integer) capLookup.get(capType)).intValue();
1027: } else {
1028: capCode = java.awt.BasicStroke.CAP_SQUARE;
1029: }
1030:
1031: // get the other properties needed for the stroke
1032: float[] dashes = stroke.getDashArray();
1033: float width = evalToFloat(stroke.getWidth(), feature, 1);
1034: float dashOffset = evalToFloat(stroke.getDashOffset(), feature,
1035: 0);
1036:
1037: // Simple optimization: let java2d use the fast drawing path if the line width
1038: // is small enough...
1039: if (width < 1.5) {
1040: width = 0;
1041: }
1042:
1043: // now set up the stroke
1044: BasicStroke stroke2d;
1045:
1046: if ((dashes != null) && (dashes.length > 0)) {
1047: stroke2d = new BasicStroke(width, capCode, joinCode, 1,
1048: dashes, dashOffset);
1049: } else {
1050: stroke2d = new BasicStroke(width, capCode, joinCode, 1);
1051: }
1052:
1053: return stroke2d;
1054: }
1055:
1056: private Paint getStrokePaint(org.geotools.styling.Stroke stroke,
1057: Object feature) {
1058: if (stroke == null) {
1059: return null;
1060: }
1061:
1062: // the foreground color
1063: Paint contourPaint = evalToColor(stroke.getColor(), feature,
1064: Color.BLACK);
1065:
1066: // if a graphic fill is to be used, prepare the paint accordingly....
1067: org.geotools.styling.Graphic gr = stroke.getGraphicFill();
1068:
1069: if (gr != null) {
1070: contourPaint = getTexturePaint(gr, feature);
1071: }
1072:
1073: return contourPaint;
1074: }
1075:
1076: private Composite getStrokeComposite(
1077: org.geotools.styling.Stroke stroke, Object feature) {
1078: if (stroke == null) {
1079: return null;
1080: }
1081:
1082: // get the opacity and prepare the composite
1083: float opacity = evalOpacity(stroke.getOpacity(), feature);
1084: Composite composite = AlphaComposite.getInstance(
1085: AlphaComposite.SRC_OVER, opacity);
1086:
1087: return composite;
1088: }
1089:
1090: protected Paint getPaint(Fill fill, Object feature) {
1091: if (fill == null) {
1092: return null;
1093: }
1094:
1095: // get fill color
1096: Paint fillPaint = null;
1097:
1098: if (fill.getColor() != null) {
1099: fillPaint = Color.decode((String) fill.getColor().evaluate(
1100: feature));
1101:
1102: if (LOGGER.isLoggable(Level.FINER)) {
1103: LOGGER.finer("Setting fill: " + fillPaint.toString());
1104: }
1105: }
1106:
1107: // if a graphic fill is to be used, prepare the paint accordingly....
1108: org.geotools.styling.Graphic gr = fill.getGraphicFill();
1109:
1110: if (gr != null) {
1111: fillPaint = getTexturePaint(gr, feature);
1112: }
1113:
1114: return fillPaint;
1115: }
1116:
1117: /**
1118: * Computes the Composite equivalent to the opacity in the SLD Fill
1119: *
1120: * @param fill
1121: * @param feature
1122: *
1123: */
1124: protected Composite getComposite(Fill fill, Object feature) {
1125: if (fill == null) {
1126: return null;
1127: }
1128:
1129: // get the opacity and prepare the composite
1130: float opacity = evalOpacity(fill.getOpacity(), feature);
1131: Composite composite = AlphaComposite.getInstance(
1132: AlphaComposite.SRC_OVER, opacity);
1133:
1134: return composite;
1135: }
1136:
1137: /**
1138: * DOCUMENT ME!
1139: *
1140: * @param gr DOCUMENT ME!
1141: * @param feature DOCUMENT ME!
1142: *
1143: * @return DOCUMENT ME!
1144: */
1145: public TexturePaint getTexturePaint(
1146: org.geotools.styling.Graphic gr, Object feature) {
1147: BufferedImage image = getExternalGraphic(gr);
1148:
1149: if (image != null) {
1150: if (LOGGER.isLoggable(Level.FINER)) {
1151: LOGGER.finer("got an image in graphic fill");
1152: }
1153: } else {
1154: if (LOGGER.isLoggable(Level.FINER)) {
1155: LOGGER.finer("going for the mark from graphic fill");
1156: }
1157:
1158: org.geotools.styling.Mark mark = getMark(gr, feature);
1159:
1160: if (mark == null) {
1161: return null;
1162: }
1163:
1164: int size = 200;
1165:
1166: image = new BufferedImage(size, size,
1167: BufferedImage.TYPE_INT_ARGB);
1168:
1169: Graphics2D g2d = image.createGraphics();
1170: double rotation = 0.0;
1171:
1172: rotation = ((Number) gr.getRotation().evaluate(feature))
1173: .doubleValue();
1174: rotation *= (Math.PI / 180.0);
1175:
1176: fillDrawMark(g2d, 100, 100, mark, (int) (size * .9),
1177: rotation, feature);
1178:
1179: java.awt.MediaTracker track = new java.awt.MediaTracker(obs);
1180: track.addImage(image, 1);
1181:
1182: try {
1183: track.waitForID(1);
1184: } catch (InterruptedException e) {
1185: // TODO: what should we do with this?
1186: LOGGER
1187: .warning("An unterupptedException occurred while drawing a local image..."
1188: + e);
1189: }
1190: }
1191:
1192: int size = ((Number) gr.getSize().evaluate(feature)).intValue();
1193: double width = image.getWidth();
1194: double height = image.getHeight();
1195:
1196: double unitSize = Math.max(width, height);
1197: double drawSize = (double) size / unitSize;
1198:
1199: width *= drawSize;
1200: height *= -drawSize;
1201:
1202: if (LOGGER.isLoggable(Level.FINER)) {
1203: LOGGER.finer("size = " + size + " unitsize " + unitSize
1204: + " drawSize " + drawSize);
1205: }
1206:
1207: Rectangle2D.Double rect = new Rectangle2D.Double(0.0, 0.0,
1208: width, height);
1209: TexturePaint imagePaint = new TexturePaint(image, rect);
1210:
1211: if (LOGGER.isLoggable(Level.FINER)) {
1212: LOGGER.finer("applied TexturePaint " + imagePaint);
1213: }
1214:
1215: return imagePaint;
1216: }
1217:
1218: private BufferedImage getExternalGraphic(Graphic graphic) {
1219: ExternalGraphic[] extgraphics = graphic.getExternalGraphics();
1220:
1221: if (extgraphics != null) {
1222: for (int i = 0; i < extgraphics.length; i++) {
1223: ExternalGraphic eg = extgraphics[i];
1224: BufferedImage img = getImage(eg, -1);
1225:
1226: if (img != null) {
1227: return img;
1228: }
1229: }
1230: }
1231:
1232: return null;
1233: }
1234:
1235: private BufferedImage getImage(ExternalGraphic eg, int sizeHint) {
1236: if (LOGGER.isLoggable(Level.FINEST)) {
1237: LOGGER.finest("got a " + eg.getFormat());
1238: }
1239:
1240: BufferedImage img;
1241:
1242: if (eg.getFormat().toLowerCase().equals("image/svg")) {
1243: try {
1244: URL svgfile = eg.getLocation();
1245: InternalTranscoder svgTranscoder = new InternalTranscoder();
1246: TranscoderInput in;
1247:
1248: if (svgGlyphs.containsKey(svgfile)) {
1249: in = new TranscoderInput((Document) svgGlyphs
1250: .get(svgfile));
1251: } else {
1252: in = new TranscoderInput(svgfile.openStream());
1253: }
1254:
1255: if (sizeHint > 0) {
1256: svgTranscoder.addTranscodingHint(
1257: SVGAbstractTranscoder.KEY_WIDTH, new Float(
1258: sizeHint));
1259: }
1260:
1261: TranscoderOutput out = new TranscoderOutput();
1262: svgTranscoder.transcode(in, out);
1263:
1264: svgGlyphs.put(svgfile, svgTranscoder.getDocument());
1265: img = svgTranscoder.getImage();
1266:
1267: return img;
1268: } catch (IOException mue) {
1269: LOGGER.warning("Unable to load external svg file, "
1270: + mue.getMessage());
1271:
1272: return null;
1273: }
1274: // catch(org.apache.batik.transcoder.TranscoderException te){
1275: // this.LOGGER.warning("Unable to render external svg file, " + te.getMessage());
1276: // return null;
1277: // }
1278: catch (Exception e) {
1279: LOGGER
1280: .warning("Unable to process or render external svg file, "
1281: + e.getMessage());
1282:
1283: return null;
1284: }
1285: }
1286:
1287: if (getSupportedGraphicFormats().contains(
1288: eg.getFormat().toLowerCase())) {
1289: if (LOGGER.isLoggable(Level.FINER)) {
1290: LOGGER.finer("a java supported format");
1291: }
1292:
1293: try {
1294: // imageLoader is not
1295: synchronized (imageLoader) {
1296: img = imageLoader.get(eg.getLocation(), false);
1297: }
1298:
1299: if (LOGGER.isLoggable(Level.FINEST)) {
1300: LOGGER.finest("Image return = " + img);
1301: }
1302:
1303: return img;
1304: } catch (java.net.MalformedURLException e) {
1305: LOGGER.warning("ExternalGraphic has a malformed url: "
1306: + e);
1307: }
1308: }
1309:
1310: return null;
1311: }
1312:
1313: private Mark getMark(Graphic graphic, Object feature) {
1314: Mark[] marks = graphic.getMarks();
1315: Mark mark;
1316:
1317: for (int i = 0; i < marks.length; i++) {
1318: String name = marks[i].getWellKnownName().evaluate(feature)
1319: .toString();
1320:
1321: if (wellKnownMarks.contains(name)) {
1322: mark = marks[i];
1323:
1324: return mark;
1325: }
1326: }
1327:
1328: mark = null;
1329:
1330: return mark;
1331: }
1332:
1333: private void fillDrawMark(Graphics2D graphic, double tx, double ty,
1334: Mark mark, int size, double rotation, Object feature) {
1335: AffineTransform temp = graphic.getTransform();
1336: AffineTransform markAT = new AffineTransform();
1337: Shape shape = Java2DMark.getWellKnownMark(mark
1338: .getWellKnownName().evaluate(feature).toString());
1339:
1340: Point2D mapCentre = new Point2D.Double(tx, ty);
1341: Point2D graphicCentre = new Point2D.Double();
1342: temp.transform(mapCentre, graphicCentre);
1343: markAT.translate(graphicCentre.getX(), graphicCentre.getY());
1344:
1345: double shearY = temp.getShearY();
1346: double scaleY = temp.getScaleY();
1347:
1348: double originalRotation = Math.atan(shearY / scaleY);
1349:
1350: if (LOGGER.isLoggable(Level.FINER)) {
1351: LOGGER.finer("originalRotation " + originalRotation);
1352: }
1353:
1354: markAT.rotate(rotation - originalRotation);
1355:
1356: double unitSize = 1.0; // getbounds is broken !!!
1357: double drawSize = (double) size / unitSize;
1358: markAT.scale(drawSize, -drawSize);
1359:
1360: graphic.setTransform(markAT);
1361:
1362: if (mark.getFill() != null) {
1363: if (LOGGER.isLoggable(Level.FINER)) {
1364: LOGGER.finer("applying fill to mark");
1365: }
1366:
1367: graphic.setPaint(getPaint(mark.getFill(), null));
1368: graphic.setComposite(getComposite(mark.getFill(), null));
1369: graphic.fill(shape);
1370: }
1371:
1372: if (mark.getStroke() != null) {
1373: if (LOGGER.isLoggable(Level.FINER)) {
1374: LOGGER.finer("applying stroke to mark");
1375: }
1376:
1377: graphic.setPaint(getStrokePaint(mark.getStroke(), null));
1378: graphic.setComposite(getStrokeComposite(mark.getStroke(),
1379: null));
1380: graphic.setStroke(getStroke(mark.getStroke(), null));
1381: graphic.draw(shape);
1382: }
1383:
1384: graphic.setTransform(temp);
1385:
1386: if (mark.getFill() != null) {
1387: graphic.setComposite(AlphaComposite.getInstance(
1388: AlphaComposite.SRC_OVER, 1.0f));
1389: }
1390:
1391: return;
1392: }
1393:
1394: /**
1395: * Evaluates an expression over the passed feature, if the expression or the result is null,
1396: * the default value will be returned
1397: *
1398: * @param e
1399: * @param drawMe
1400: * @param defaultValue
1401: *
1402: */
1403: private String evaluateExpression(
1404: org.opengis.filter.expression.Expression e, Object drawMe,
1405: String defaultValue) {
1406: String result = defaultValue;
1407:
1408: if (e != null) {
1409: result = (String) e.evaluate(drawMe);
1410:
1411: if (result == null) {
1412: result = defaultValue;
1413: }
1414: }
1415:
1416: return result;
1417: }
1418:
1419: /**
1420: * DOCUMENT ME!
1421: *
1422: * @param joinType DOCUMENT ME!
1423: *
1424: * @return DOCUMENT ME!
1425: */
1426: public static int lookUpJoin(String joinType) {
1427: if (SLDStyleFactory.joinLookup.containsKey(joinType)) {
1428: return ((Integer) joinLookup.get(joinType)).intValue();
1429: } else {
1430: return java.awt.BasicStroke.JOIN_MITER;
1431: }
1432: }
1433:
1434: /**
1435: * DOCUMENT ME!
1436: *
1437: * @param capType DOCUMENT ME!
1438: *
1439: * @return DOCUMENT ME!
1440: */
1441: public static int lookUpCap(String capType) {
1442: if (SLDStyleFactory.capLookup.containsKey(capType)) {
1443: return ((Integer) capLookup.get(capType)).intValue();
1444: } else {
1445: return java.awt.BasicStroke.CAP_SQUARE;
1446: }
1447: }
1448:
1449: /**
1450: * Getter for property mapScaleDenominator.
1451: * @return Value of property mapScaleDenominator.
1452: */
1453: public double getMapScaleDenominator() {
1454:
1455: return this .mapScaleDenominator;
1456: }
1457:
1458: /**
1459: * Setter for property mapScaleDenominator.
1460: * @param mapScaleDenominator New value of property mapScaleDenominator.
1461: */
1462: public void setMapScaleDenominator(double mapScaleDenominator) {
1463:
1464: this .mapScaleDenominator = mapScaleDenominator;
1465: }
1466:
1467: /**
1468: * Simple key used to cache Style2D objects based on the originating symbolizer and scale
1469: * range. Will compare symbolizers by identity, avoiding a possibly very long comparison
1470: *
1471: * @author aaime
1472: */
1473: private static class SymbolizerKey {
1474: private Symbolizer symbolizer;
1475: private double minScale;
1476: private double maxScale;
1477:
1478: public SymbolizerKey(Symbolizer symbolizer, Range scaleRange) {
1479: this .symbolizer = symbolizer;
1480: minScale = ((Number) scaleRange.getMinValue())
1481: .doubleValue();
1482: maxScale = ((Number) scaleRange.getMaxValue())
1483: .doubleValue();
1484: }
1485:
1486: /**
1487: * @see java.lang.Object#equals(java.lang.Object)
1488: */
1489: public boolean equals(Object obj) {
1490: if (!(obj instanceof SymbolizerKey)) {
1491: return false;
1492: }
1493:
1494: SymbolizerKey other = (SymbolizerKey) obj;
1495:
1496: return (other.symbolizer == symbolizer)
1497: && (other.minScale == minScale)
1498: && (other.maxScale == maxScale);
1499: }
1500:
1501: /**
1502: * @see java.lang.Object#hashCode()
1503: */
1504: public int hashCode() {
1505: return ((((17 + System.identityHashCode(symbolizer)) * 37) + doubleHash(minScale)) * 37)
1506: + doubleHash(maxScale);
1507: }
1508:
1509: private int doubleHash(double value) {
1510: long bits = Double.doubleToLongBits(value);
1511:
1512: return (int) (bits ^ (bits >>> 32));
1513: }
1514: }
1515:
1516: private float evalToFloat(Expression exp, Object f, float fallback) {
1517: if (exp == null) {
1518: return fallback;
1519: }
1520: Object o = exp.evaluate(f);
1521: if (o instanceof Number)
1522: return ((Number) o).floatValue();
1523: Float fo = (Float) exp.evaluate(f, Float.class);
1524: if (fo != null) {
1525: return fo.floatValue();
1526: }
1527: return fallback;
1528: }
1529:
1530: private double evalToDouble(Expression exp, Object f,
1531: double fallback) {
1532:
1533: if (exp == null) {
1534: return fallback;
1535: }
1536: Object o = exp.evaluate(f);
1537: if (o instanceof Number)
1538: return ((Number) o).doubleValue();
1539: Double d = (Double) exp.evaluate(f, Double.class);
1540: if (d != null) {
1541: return d.doubleValue();
1542: }
1543: return fallback;
1544: }
1545:
1546: private Color evalToColor(Expression exp, Object f, Color fallback) {
1547: if (exp == null) {
1548: return fallback;
1549: }
1550: try {
1551: return Color.decode((String) exp.evaluate(f));
1552: } catch (NumberFormatException nfe) {
1553: return fallback;
1554: }
1555: }
1556:
1557: private float evalOpacity(Expression e, Object f) {
1558: return evalToFloat(e, f, 1);
1559: }
1560:
1561: }
|