0001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
0002: * This code is licensed under the GPL 2.0 license, availible at the root
0003: * application directory.
0004: */
0005: package org.vfny.geoserver.wms.responses.map.svg;
0006:
0007: import com.vividsolutions.jts.geom.Coordinate;
0008: import com.vividsolutions.jts.geom.Envelope;
0009: import com.vividsolutions.jts.geom.Geometry;
0010: import com.vividsolutions.jts.geom.LineString;
0011: import com.vividsolutions.jts.geom.LinearRing;
0012: import com.vividsolutions.jts.geom.MultiLineString;
0013: import com.vividsolutions.jts.geom.MultiPoint;
0014: import com.vividsolutions.jts.geom.MultiPolygon;
0015: import com.vividsolutions.jts.geom.Point;
0016: import com.vividsolutions.jts.geom.Polygon;
0017: import org.geotools.data.DataSourceException;
0018: import org.geotools.data.FeatureReader;
0019: import org.geotools.feature.Feature;
0020: import org.geotools.feature.FeatureIterator;
0021: import org.geotools.feature.FeatureType;
0022: import org.geotools.feature.IllegalAttributeException;
0023: import org.vfny.geoserver.wms.WMSMapContext;
0024: import java.io.IOException;
0025: import java.io.OutputStream;
0026: import java.io.OutputStreamWriter;
0027: import java.text.DecimalFormat;
0028: import java.text.DecimalFormatSymbols;
0029: import java.util.ArrayList;
0030: import java.util.HashMap;
0031: import java.util.List;
0032: import java.util.Locale;
0033: import java.util.NoSuchElementException;
0034: import java.util.logging.Logger;
0035:
0036: /**
0037: * DOCUMENT ME!
0038: *
0039: * @author Gabriel Rold?n
0040: * @version $Id: SVGWriter.java 7746 2007-11-13 15:38:35Z aaime $
0041: */
0042: public class SVGWriter extends OutputStreamWriter {
0043: private static final Logger LOGGER = org.geotools.util.logging.Logging
0044: .getLogger(SVGWriter.class.getPackage().getName());
0045:
0046: /**
0047: * a number formatter setted up to write SVG legible numbers ('.' as
0048: * decimal separator, no group separator
0049: */
0050: private static DecimalFormat formatter;
0051:
0052: /**
0053: * map of geometry class to writer
0054: */
0055: private HashMap writers;
0056:
0057: static {
0058: Locale locale = new Locale("en", "US");
0059: DecimalFormatSymbols decimalSymbols = new DecimalFormatSymbols(
0060: locale);
0061: decimalSymbols.setDecimalSeparator('.');
0062: formatter = new DecimalFormat();
0063: formatter.setDecimalFormatSymbols(decimalSymbols);
0064:
0065: //do not group
0066: formatter.setGroupingSize(0);
0067:
0068: //do not show decimal separator if it is not needed
0069: formatter.setDecimalSeparatorAlwaysShown(false);
0070: formatter.setDecimalFormatSymbols(null);
0071:
0072: //set default number of fraction digits
0073: formatter.setMaximumFractionDigits(5);
0074:
0075: //minimun fraction digits to 0 so they get not rendered if not needed
0076: formatter.setMinimumFractionDigits(0);
0077: }
0078:
0079: /** DOCUMENT ME! */
0080: private double minY;
0081:
0082: /** DOCUMENT ME! */
0083: private double maxY;
0084:
0085: /** DOCUMENT ME! */
0086: private int coordsSkipCount;
0087:
0088: /** DOCUMENT ME! */
0089: private int coordsWriteCount;
0090:
0091: /** DOCUMENT ME! */
0092: private SVGFeatureWriterHandler writerHandler = new SVGFeatureWriterHandler();
0093:
0094: /** DOCUMENT ME! */
0095: private SVGFeatureWriter featureWriter = null;
0096:
0097: /** DOCUMENT ME! */
0098: private double minCoordDistance;
0099:
0100: /** DOCUMENT ME! */
0101: private String attributeStyle;
0102:
0103: /** DOCUMENT ME! */
0104: private boolean pointsAsCircles;
0105:
0106: /** DOCUMENT ME! */
0107: private WMSMapContext mapContext;
0108:
0109: /**
0110: * Creates a new SVGWriter object.
0111: *
0112: * @param out DOCUMENT ME!
0113: * @param config DOCUMENT ME!
0114: */
0115: public SVGWriter(OutputStream out, WMSMapContext mapContext) {
0116: super (out);
0117: this .mapContext = mapContext;
0118:
0119: Envelope space = mapContext.getAreaOfInterest();
0120: this .minY = space.getMinY();
0121: this .maxY = space.getMaxY();
0122:
0123: initWriters();
0124: }
0125:
0126: private void initWriters() {
0127: writers = new HashMap();
0128: writers.put(Point.class, new PointWriter());
0129: writers.put(LineString.class, new LineStringWriter());
0130: writers.put(LinearRing.class, new LineStringWriter());
0131: writers.put(Polygon.class, new PolygonWriter());
0132: writers.put(MultiPoint.class, new MultiPointWriter());
0133: writers.put(MultiLineString.class, new MultiLineStringWriter());
0134: writers.put(MultiPolygon.class, new MultiPolygonWriter());
0135: }
0136:
0137: /**
0138: * DOCUMENT ME!
0139: *
0140: * @param attributeName DOCUMENT ME!
0141: */
0142: public void setAttributeStyle(String attributeName) {
0143: this .attributeStyle = attributeName;
0144: }
0145:
0146: /**
0147: * DOCUMENT ME!
0148: *
0149: * @param asCircles DOCUMENT ME!
0150: */
0151: public void setPointsAsCircles(boolean asCircles) {
0152: this .pointsAsCircles = asCircles;
0153: }
0154:
0155: /**
0156: * DOCUMENT ME!
0157: *
0158: * @param gtype DOCUMENT ME!
0159: *
0160: * @throws IllegalArgumentException DOCUMENT ME!
0161: */
0162: public void setGeometryType(Class gtype) {
0163: featureWriter = (SVGFeatureWriter) writers.get(gtype);
0164:
0165: if (featureWriter == null) {
0166: //check for abstract Geometry type
0167: if (gtype == Geometry.class) {
0168: featureWriter = new GeometryWriter();
0169: } else {
0170: throw new IllegalArgumentException(
0171: "No SVG Feature writer defined for " + gtype);
0172: }
0173: }
0174:
0175: // if (gtype == Point.class) {
0176: // featureWriter = new PointWriter();
0177: // } else if (gtype == MultiPoint.class) {
0178: // featureWriter = new MultiPointWriter();
0179: // } else if (gtype == LineString.class) {
0180: // featureWriter = new LineStringWriter();
0181: // } else if (gtype == MultiLineString.class) {
0182: // featureWriter = new MultiLineStringWriter();
0183: // } else if (gtype == Polygon.class) {
0184: // featureWriter = new PolygonWriter();
0185: // } else if (gtype == MultiPolygon.class) {
0186: // featureWriter = new MultiPolygonWriter();
0187: // } else {
0188: // throw new IllegalArgumentException(
0189: // "No SVG Feature writer defined for " + gtype);
0190: // }
0191:
0192: /*
0193: if (config.isCollectGeometries()) {
0194: this.writerHandler = new CollectSVGHandler(featureWriter);
0195: } else {
0196: this.writerHandler = new SVGFeatureWriterHandler();
0197: this.writerHandler = new FIDSVGHandler(this.writerHandler);
0198: this.writerHandler = new BoundsSVGHandler(this.writerHandler);
0199: this.writerHandler = new AttributesSVGHandler(this.writerHandler);
0200: }
0201: */
0202: }
0203:
0204: public void setWriterHandler(SVGFeatureWriterHandler handler) {
0205: this .writerHandler = handler;
0206: }
0207:
0208: /**
0209: * DOCUMENT ME!
0210: *
0211: * @param minCoordDistance DOCUMENT ME!
0212: */
0213: public void setMinCoordDistance(double minCoordDistance) {
0214: this .minCoordDistance = minCoordDistance;
0215: }
0216:
0217: /**
0218: * if a reference space has been set, returns a translated Y coordinate
0219: * wich is inverted based on the height of such a reference space,
0220: * otherwise just returns <code>y</code>
0221: *
0222: * @param y DOCUMENT ME!
0223: *
0224: * @return DOCUMENT ME!
0225: */
0226: public double getY(double y) {
0227: return (maxY - y) + minY;
0228: }
0229:
0230: /**
0231: * DOCUMENT ME!
0232: *
0233: * @param x DOCUMENT ME!
0234: *
0235: * @return DOCUMENT ME!
0236: */
0237: public double getX(double x) {
0238: return x;
0239: }
0240:
0241: /**
0242: * DOCUMENT ME!
0243: *
0244: * @param numDigits DOCUMENT ME!
0245: */
0246: public void setMaximunFractionDigits(int numDigits) {
0247: formatter.setMaximumFractionDigits(numDigits);
0248: }
0249:
0250: /**
0251: * DOCUMENT ME!
0252: *
0253: * @return DOCUMENT ME!
0254: */
0255: public int getMaximunFractionDigits() {
0256: return formatter.getMaximumFractionDigits();
0257: }
0258:
0259: /**
0260: * DOCUMENT ME!
0261: *
0262: * @param numDigits DOCUMENT ME!
0263: */
0264: public void setMinimunFractionDigits(int numDigits) {
0265: formatter.setMinimumFractionDigits(numDigits);
0266: }
0267:
0268: /**
0269: * DOCUMENT ME!
0270: *
0271: * @return DOCUMENT ME!
0272: */
0273: public int getMinimunFractionDigits() {
0274: return formatter.getMinimumFractionDigits();
0275: }
0276:
0277: /**
0278: * DOCUMENT ME!
0279: *
0280: * @param d DOCUMENT ME!
0281: *
0282: * @throws IOException DOCUMENT ME!
0283: */
0284: public void write(double d) throws IOException {
0285: write(formatter.format(d));
0286: }
0287:
0288: /**
0289: * DOCUMENT ME!
0290: *
0291: * @param c DOCUMENT ME!
0292: *
0293: * @throws IOException DOCUMENT ME!
0294: */
0295: public void write(char c) throws IOException {
0296: super .write(c);
0297: }
0298:
0299: /**
0300: * DOCUMENT ME!
0301: *
0302: * @throws IOException DOCUMENT ME!
0303: */
0304: public void newline() throws IOException {
0305: super .write('\n');
0306: }
0307:
0308: public void writeFeatures(FeatureType featureType,
0309: FeatureIterator reader, String style) throws IOException,
0310: AbortedException {
0311: Feature ft;
0312:
0313: try {
0314: Class gtype = featureType.getDefaultGeometry().getType();
0315:
0316: boolean doCollect = false;
0317: /*
0318: boolean doCollect = config.isCollectGeometries()
0319: && (gtype != Point.class) && (gtype != MultiPoint.class);
0320: */
0321: setGeometryType(gtype);
0322:
0323: setPointsAsCircles("#circle".equals(style));
0324:
0325: if ((style != null) && !"#circle".equals(style)
0326: && style.startsWith("#")) {
0327: style = style.substring(1);
0328: } else {
0329: style = null;
0330: }
0331:
0332: setAttributeStyle(style);
0333:
0334: setUpWriterHandler(featureType, doCollect);
0335:
0336: if (doCollect) {
0337: write("<path ");
0338: write("d=\"");
0339: }
0340:
0341: while (reader.hasNext()) {
0342: ft = reader.next();
0343: writeFeature(ft);
0344: ft = null;
0345: }
0346:
0347: if (doCollect) {
0348: write("\"/>\n");
0349: }
0350:
0351: LOGGER.fine("encoded " + featureType.getTypeName());
0352: } catch (NoSuchElementException ex) {
0353: throw new DataSourceException(ex.getMessage(), ex);
0354: } catch (Exception ex) {
0355: throw new DataSourceException(ex.getMessage(), ex);
0356: }
0357: }
0358:
0359: private void setUpWriterHandler(FeatureType featureType,
0360: boolean doCollect) throws IOException {
0361: if (doCollect) {
0362: this .writerHandler = new CollectSVGHandler(featureWriter);
0363: LOGGER
0364: .finer("Established a collecting features writer handler");
0365: } else {
0366: this .writerHandler = new SVGFeatureWriterHandler();
0367:
0368: String typeName = featureType.getTypeName();
0369:
0370: /*
0371: * REVISIT: get rid of all this attribute stuff, since if attributes are
0372: * needed it fits better to have SVG with gml attributes as another output
0373: * format for WFS's getFeature.
0374: */
0375: List atts = new ArrayList(0); // config.getAttributes(typeName);
0376:
0377: if (atts.contains("#FID")) {
0378: this .writerHandler = new FIDSVGHandler(
0379: this .writerHandler);
0380: atts.remove("#FID");
0381: LOGGER.finer("Added FID handler decorator");
0382: }
0383:
0384: if (atts.contains("#BOUNDS")) {
0385: this .writerHandler = new BoundsSVGHandler(
0386: this .writerHandler);
0387: atts.remove("#BOUNDS");
0388: LOGGER.finer("Added BOUNDS handler decorator");
0389: }
0390:
0391: if (atts.size() > 0) {
0392: this .writerHandler = new AttributesSVGHandler(
0393: this .writerHandler);
0394: LOGGER.finer("Added ATTRIBUTES handler decorator");
0395: }
0396: }
0397: }
0398:
0399: /**
0400: * DOCUMENT ME!
0401: *
0402: * @param ft
0403: *
0404: * @throws IOException si algo ocurre escribiendo a <code>out</code>
0405: */
0406: public void writeFeature(Feature ft) throws IOException {
0407: writerHandler.startFeature(featureWriter, ft);
0408: writerHandler.startGeometry(featureWriter, ft);
0409: writerHandler.writeGeometry(featureWriter, ft);
0410: writerHandler.endGeometry(featureWriter, ft);
0411: writerHandler.endFeature(featureWriter, ft);
0412: }
0413:
0414: /**
0415: * DOCUMENT ME!
0416: *
0417: * @author $author$
0418: * @version $Revision: 1.5 $
0419: */
0420: public class SVGFeatureWriterHandler {
0421: /**
0422: * DOCUMENT ME!
0423: *
0424: * @param featureWriter DOCUMENT ME!
0425: * @param ft DOCUMENT ME!
0426: *
0427: * @throws IOException DOCUMENT ME!
0428: */
0429: public void startFeature(SVGFeatureWriter featureWriter,
0430: Feature ft) throws IOException {
0431: featureWriter.startElement(ft);
0432: }
0433:
0434: /**
0435: * DOCUMENT ME!
0436: *
0437: * @param featureWriter DOCUMENT ME!
0438: * @param ft DOCUMENT ME!
0439: *
0440: * @throws IOException DOCUMENT ME!
0441: */
0442: public void endFeature(SVGFeatureWriter featureWriter,
0443: Feature ft) throws IOException {
0444: featureWriter.endElement(ft);
0445: }
0446:
0447: /**
0448: * DOCUMENT ME!
0449: *
0450: * @param featureWriter DOCUMENT ME!
0451: * @param ft DOCUMENT ME!
0452: *
0453: * @throws IOException DOCUMENT ME!
0454: */
0455: public void startGeometry(SVGFeatureWriter featureWriter,
0456: Feature ft) throws IOException {
0457: featureWriter.startGeometry(ft.getDefaultGeometry());
0458: }
0459:
0460: /**
0461: * DOCUMENT ME!
0462: *
0463: * @param featureWriter DOCUMENT ME!
0464: * @param ft DOCUMENT ME!
0465: *
0466: * @throws IOException DOCUMENT ME!
0467: */
0468: public void writeGeometry(SVGFeatureWriter featureWriter,
0469: Feature ft) throws IOException {
0470: featureWriter.writeGeometry(ft.getDefaultGeometry());
0471: }
0472:
0473: /**
0474: * DOCUMENT ME!
0475: *
0476: * @param featureWriter DOCUMENT ME!
0477: * @param ft DOCUMENT ME!
0478: *
0479: * @throws IOException DOCUMENT ME!
0480: */
0481: public void endGeometry(SVGFeatureWriter featureWriter,
0482: Feature ft) throws IOException {
0483: featureWriter.endGeometry(ft.getDefaultGeometry());
0484: }
0485: }
0486:
0487: /**
0488: * DOCUMENT ME!
0489: *
0490: * @author $author$
0491: * @version $Revision: 1.5 $
0492: */
0493: public class CollectSVGHandler extends SVGFeatureWriterHandler {
0494: /** DOCUMENT ME! */
0495: private SVGFeatureWriter featureWriter;
0496:
0497: /**
0498: * Creates a new CollectSVGHandler object.
0499: *
0500: * @param featureWriter DOCUMENT ME!
0501: */
0502: public CollectSVGHandler(SVGFeatureWriter featureWriter) {
0503: this .featureWriter = featureWriter;
0504: }
0505:
0506: /**
0507: * DOCUMENT ME!
0508: *
0509: * @param ft DOCUMENT ME!
0510: *
0511: * @throws IOException DOCUMENT ME!
0512: */
0513: public void writeFeature(Feature ft) throws IOException {
0514: featureWriter.writeGeometry(ft.getDefaultGeometry());
0515: write('\n');
0516: }
0517: }
0518:
0519: /**
0520: * decorator handler that adds the feature id as the "id" attribute
0521: */
0522: public class FIDSVGHandler extends SVGFeatureWriterHandler {
0523: /** DOCUMENT ME! */
0524: private SVGFeatureWriterHandler handler;
0525:
0526: /**
0527: * Creates a new NormalSVGHandler object.
0528: *
0529: * @param handler DOCUMENT ME!
0530: */
0531: public FIDSVGHandler(SVGFeatureWriterHandler handler) {
0532: this .handler = handler;
0533: }
0534:
0535: /**
0536: * DOCUMENT ME!
0537: *
0538: * @param featureWriter DOCUMENT ME!
0539: * @param ft DOCUMENT ME!
0540: *
0541: * @throws IOException DOCUMENT ME!
0542: */
0543: public void startFeature(SVGFeatureWriter featureWriter,
0544: Feature ft) throws IOException {
0545: handler.startFeature(featureWriter, ft);
0546: write(" id=\"");
0547:
0548: try {
0549: write(ft.getID());
0550: } catch (IOException ex) {
0551: System.err.println("error getting fid from " + ft);
0552: throw ex;
0553: }
0554:
0555: write("\"");
0556: }
0557: }
0558:
0559: /**
0560: * decorator handler that adds the feature id as the "id" attribute
0561: */
0562: public class BoundsSVGHandler extends SVGFeatureWriterHandler {
0563: /** DOCUMENT ME! */
0564: private SVGFeatureWriterHandler handler;
0565:
0566: /**
0567: * Creates a new NormalSVGHandler object.
0568: *
0569: * @param handler DOCUMENT ME!
0570: */
0571: public BoundsSVGHandler(SVGFeatureWriterHandler handler) {
0572: this .handler = handler;
0573: }
0574:
0575: /**
0576: * DOCUMENT ME!
0577: *
0578: * @param featureWriter DOCUMENT ME!
0579: * @param ft DOCUMENT ME!
0580: *
0581: * @throws IOException DOCUMENT ME!
0582: */
0583: public void startFeature(SVGFeatureWriter featureWriter,
0584: Feature ft) throws IOException {
0585: handler.startFeature(featureWriter, ft);
0586:
0587: Geometry geom = ft.getDefaultGeometry();
0588: Envelope env = geom.getEnvelopeInternal();
0589: write(" bounds=\"");
0590: write(env.getMinX());
0591: write(' ');
0592: write(env.getMinY());
0593: write(' ');
0594: write(env.getMaxX());
0595: write(' ');
0596: write(env.getMaxY());
0597: write('\"');
0598: }
0599: }
0600:
0601: /**
0602: * decorator handler that adds the feature id as the "id" attribute
0603: */
0604: public class AttributesSVGHandler extends SVGFeatureWriterHandler {
0605: /** DOCUMENT ME! */
0606: private SVGFeatureWriterHandler handler;
0607:
0608: /**
0609: * Creates a new NormalSVGHandler object.
0610: *
0611: * @param handler DOCUMENT ME!
0612: */
0613: public AttributesSVGHandler(SVGFeatureWriterHandler handler) {
0614: this .handler = handler;
0615: }
0616:
0617: /**
0618: * DOCUMENT ME!
0619: *
0620: * @param featureWriter DOCUMENT ME!
0621: * @param ft DOCUMENT ME!
0622: *
0623: * @throws IOException DOCUMENT ME!
0624: */
0625: public void startFeature(SVGFeatureWriter featureWriter,
0626: Feature ft) throws IOException {
0627: handler.startFeature(featureWriter, ft);
0628:
0629: FeatureType type = ft.getFeatureType();
0630: int numAtts = type.getAttributeCount();
0631: String name;
0632: Object value;
0633:
0634: for (int i = 0; i < numAtts; i++) {
0635: value = ft.getAttribute(i);
0636:
0637: if ((value != null) && !(value instanceof Geometry)) {
0638: write(' ');
0639: write(type.getAttributeType(i).getName());
0640: write("=\"");
0641: encodeAttribute(String.valueOf(value));
0642: write('\"');
0643: }
0644: }
0645: }
0646:
0647: /**
0648: * Parses the passed string, and encodes the special characters (used
0649: * in xml for special purposes) with the appropriate codes. e.g.
0650: * '<' is changed to '&lt;'
0651: *
0652: * @param inData The string to encode into xml.
0653: *
0654: * @throws IOException DOCUMENT ME!
0655: *
0656: * @task REVISIT: Once we write directly to out, as we should, this
0657: * method should be simpler, as we can just write strings with
0658: * escapes directly to out, replacing as we iterate of chars to
0659: * write them.
0660: */
0661: private void encodeAttribute(String inData) throws IOException {
0662: //return null, if null is passed as argument
0663: if (inData == null) {
0664: return;
0665: }
0666:
0667: //get the length of input String
0668: int length = inData.length();
0669:
0670: char charToCompare;
0671:
0672: //iterate over the input String
0673: for (int i = 0; i < length; i++) {
0674: charToCompare = inData.charAt(i);
0675:
0676: //if the ith character is special character, replace by code
0677: if (charToCompare == '"') {
0678: write(""");
0679: } else if (charToCompare > 127) {
0680: writeUnicodeEscapeSequence(charToCompare);
0681: } else {
0682: write(charToCompare);
0683: }
0684: }
0685: }
0686:
0687: /**
0688: * returns the xml unicode escape sequence for the character
0689: * <code>c</code>, such as <code>"Ñ"</code> for the character
0690: * <code>'?'</code>
0691: *
0692: * @param c DOCUMENT ME!
0693: *
0694: * @throws IOException DOCUMENT ME!
0695: */
0696: private void writeUnicodeEscapeSequence(char c)
0697: throws IOException {
0698: write("&#x");
0699:
0700: String hex = Integer.toHexString(c);
0701: int pendingZeros = 4 - hex.length();
0702:
0703: for (int i = 0; i < pendingZeros; i++)
0704: write('0');
0705:
0706: write(hex);
0707: write(';');
0708: }
0709: }
0710:
0711: /**
0712: * DOCUMENT ME!
0713: *
0714: * @author $author$
0715: * @version $Revision: 1.5 $
0716: */
0717: private abstract class SVGFeatureWriter {
0718: /**
0719: * DOCUMENT ME!
0720: * @param feature TODO
0721: *
0722: * @throws IOException DOCUMENT ME!
0723: */
0724: protected abstract void startElement(Feature feature)
0725: throws IOException;
0726:
0727: /**
0728: * DOCUMENT ME!
0729: * @param geom TODO
0730: *
0731: * @throws IOException DOCUMENT ME!
0732: */
0733: protected abstract void startGeometry(Geometry geom)
0734: throws IOException;
0735:
0736: /**
0737: * DOCUMENT ME!
0738: *
0739: * @param geom DOCUMENT ME!
0740: *
0741: * @throws IOException DOCUMENT ME!
0742: */
0743: protected abstract void writeGeometry(Geometry geom)
0744: throws IOException;
0745:
0746: /**
0747: * DOCUMENT ME!
0748: * @param geom TODO
0749: *
0750: * @throws IOException DOCUMENT ME!
0751: */
0752: protected void endGeometry(Geometry geom) throws IOException {
0753: write("\"");
0754: }
0755:
0756: /**
0757: * DOCUMENT ME!
0758: * @param feature TODO
0759: *
0760: * @throws IOException DOCUMENT ME!
0761: */
0762: protected void endElement(Feature feature) throws IOException {
0763: write("/>\n");
0764: }
0765:
0766: /**
0767: * Writes the content of the <b>d</b> attribute in a <i>path</i> SVG
0768: * element
0769: *
0770: * <p>
0771: * While iterating over the coordinate array passed as parameter, this
0772: * method performs a kind of very basic path generalization, verifying
0773: * that the distance between the current coordinate and the last
0774: * encoded one is greater than the minimun distance expressed by the
0775: * field <code>minCoordDistance</code> and established by the method
0776: * {@link #setReferenceSpace(Envelope, float)
0777: * setReferenceSpace(Envelope, blurFactor)}
0778: * </p>
0779: *
0780: * @param coords
0781: *
0782: * @throws IOException
0783: */
0784: protected void writePathContent(Coordinate[] coords)
0785: throws IOException {
0786: write('M');
0787:
0788: Coordinate prev = coords[0];
0789: Coordinate curr = null;
0790: write(getX(prev.x));
0791: write(' ');
0792: write(getY(prev.y));
0793:
0794: int nCoords = coords.length;
0795: write('l');
0796:
0797: for (int i = 1; i < nCoords; i++) {
0798: curr = coords[i];
0799:
0800: //let at least 3 points in case it is a polygon
0801: if ((i > 3)
0802: && (prev.distance(curr) <= minCoordDistance)) {
0803: ++coordsSkipCount;
0804:
0805: continue;
0806: }
0807:
0808: ++coordsWriteCount;
0809: write((getX(curr.x) - getX(prev.x)));
0810: write(' ');
0811: write(getY(curr.y) - getY(prev.y));
0812: write(' ');
0813: prev = curr;
0814: }
0815: }
0816:
0817: /**
0818: * DOCUMENT ME!
0819: *
0820: * @param coords DOCUMENT ME!
0821: *
0822: * @throws IOException DOCUMENT ME!
0823: */
0824: protected void writeClosedPathContent(Coordinate[] coords)
0825: throws IOException {
0826: writePathContent(coords);
0827: write('Z');
0828: }
0829: }
0830:
0831: /**
0832: *
0833: */
0834: private class PointWriter extends SVGFeatureWriter {
0835: /**
0836: * Creates a new PointWriter object.
0837: */
0838: public PointWriter() {
0839: }
0840:
0841: /**
0842: * DOCUMENT ME!
0843: *
0844: * @throws IOException DOCUMENT ME!
0845: */
0846: protected void startElement(Feature feature) throws IOException {
0847: write(pointsAsCircles ? "<circle r='0.25%' fill='blue'"
0848: : "<use");
0849: }
0850:
0851: /**
0852: * DOCUMENT ME!
0853: *
0854: * @throws IOException DOCUMENT ME!
0855: */
0856:
0857: /**
0858: * protected void writeAttributes(Feature ft) throws IOException { if
0859: * (!pointsAsCircles) { write(" xlink:href=\"#"); if (attributeStyle
0860: * != null) { write(String.valueOf(ft.getAttribute(attributeStyle)));
0861: * } else { write("point"); } write("\""); }
0862: * super.writeAttributes(ft); }
0863: *
0864: * @throws IOException DOCUMENT ME!
0865: */
0866:
0867: /**
0868: * DOCUMENT ME!
0869: *
0870: * @throws IOException DOCUMENT ME!
0871: */
0872: protected void startGeometry(Geometry geom) throws IOException {
0873: }
0874:
0875: /**
0876: * overrides writeBounds for points to do nothing. You can get the
0877: * position of the point with the x and y attributes of the "use" SVG
0878: * element written to represent each point
0879: *
0880: * @param env DOCUMENT ME!
0881: *
0882: * @throws IOException DOCUMENT ME!
0883: */
0884: protected void writeBounds(Envelope env) throws IOException {
0885: }
0886:
0887: /**
0888: * DOCUMENT ME!
0889: *
0890: * @param geom DOCUMENT ME!
0891: *
0892: * @throws IOException DOCUMENT ME!
0893: */
0894: protected void writeGeometry(Geometry geom) throws IOException {
0895: Point p = (Point) geom;
0896:
0897: if (pointsAsCircles) {
0898: write(" cx=\"");
0899: write(getX(p.getX()));
0900: write("\" cy=\"");
0901: write(getY(p.getY()));
0902: } else {
0903: write(" x=\"");
0904: write(getX(p.getX()));
0905: write("\" y=\"");
0906: write(getY(p.getY()));
0907: //Issue GEOS-193, from John Steining.
0908: write("\" xlink:href=\"#point");
0909:
0910: //putting this in to fix the issue, but I am not sure about
0911: //the broader implications - I don't think we need it for
0912: //pointsAsCircles. And it looks like the quote gets closed
0913: //somewhere else, but I'm not sure where.
0914: }
0915: }
0916: }
0917:
0918: /**
0919: *
0920: */
0921: private class MultiPointWriter extends PointWriter {
0922: /**
0923: * Creates a new MultiPointWriter object.
0924: */
0925: public MultiPointWriter() {
0926: }
0927:
0928: /**
0929: * DOCUMENT ME!
0930: *
0931: * @throws IOException DOCUMENT ME!
0932: */
0933: protected void startElement(Feature feature) throws IOException {
0934: write("<g ");
0935: }
0936:
0937: /**
0938: * DOCUMENT ME!
0939: *
0940: * @throws IOException DOCUMENT ME!
0941: */
0942: protected void startGeometry(Geometry geom) throws IOException {
0943: write("/>\n");
0944: }
0945:
0946: /**
0947: * DOCUMENT ME!
0948: *
0949: * @param geom DOCUMENT ME!
0950: *
0951: * @throws IOException DOCUMENT ME!
0952: */
0953: protected void writeGeometry(Geometry geom) throws IOException {
0954: MultiPoint mp = (MultiPoint) geom;
0955:
0956: for (int i = 0; i < mp.getNumGeometries(); i++) {
0957: super .startElement(null);
0958: super .writeGeometry(mp.getGeometryN(i));
0959: super .endGeometry(mp.getGeometryN(i));
0960: super .endElement(null);
0961: }
0962: }
0963:
0964: /**
0965: * DOCUMENT ME!
0966: *
0967: * @throws IOException DOCUMENT ME!
0968: */
0969: protected void endElement(Feature feature) throws IOException {
0970: write("</g>\n");
0971: }
0972: }
0973:
0974: /**
0975: * Writer to handle feature types which contain a Geometry attribute that
0976: * is actually of the class Geometry. This can occur in heterogeneous data
0977: * sets.
0978: *
0979: * @author Justin Deoliveira, jdeolive@openplans.org
0980: *
0981: */
0982: private class GeometryWriter extends SVGFeatureWriter {
0983: SVGFeatureWriter delegate;
0984:
0985: protected void startElement(Feature feature) throws IOException {
0986: Geometry g = feature.getDefaultGeometry();
0987: delegate = null;
0988:
0989: if (g != null) {
0990: delegate = (SVGFeatureWriter) writers.get(g.getClass());
0991: }
0992:
0993: if (delegate == null) {
0994: throw new IllegalArgumentException(
0995: "No SVG Feature writer defined for " + g);
0996: }
0997:
0998: delegate.startElement(feature);
0999: }
1000:
1001: protected void startGeometry(Geometry geom) throws IOException {
1002: delegate.startGeometry(geom);
1003: }
1004:
1005: protected void writeGeometry(Geometry geom) throws IOException {
1006: delegate.writeGeometry(geom);
1007: }
1008: }
1009:
1010: /**
1011: *
1012: */
1013: private class LineStringWriter extends SVGFeatureWriter {
1014: /**
1015: * Creates a new LineStringWriter object.
1016: */
1017: public LineStringWriter() {
1018: }
1019:
1020: /**
1021: * DOCUMENT ME!
1022: *
1023: * @throws IOException DOCUMENT ME!
1024: */
1025: protected void startElement(Feature feature) throws IOException {
1026: write("<path");
1027: }
1028:
1029: /**
1030: * DOCUMENT ME!
1031: *
1032: * @throws IOException DOCUMENT ME!
1033: */
1034: protected void startGeometry(Geometry geom) throws IOException {
1035: write(" d=\"");
1036: }
1037:
1038: /**
1039: * DOCUMENT ME!
1040: *
1041: * @param geom DOCUMENT ME!
1042: *
1043: * @throws IOException DOCUMENT ME!
1044: */
1045: protected void writeGeometry(Geometry geom) throws IOException {
1046: writePathContent(((LineString) geom).getCoordinates());
1047: }
1048: }
1049:
1050: /**
1051: *
1052: */
1053: private class MultiLineStringWriter extends LineStringWriter {
1054: /**
1055: * Creates a new MultiLineStringWriter object.
1056: */
1057: public MultiLineStringWriter() {
1058: }
1059:
1060: /**
1061: * DOCUMENT ME!
1062: *
1063: * @param geom DOCUMENT ME!
1064: *
1065: * @throws IOException DOCUMENT ME!
1066: */
1067: protected void writeGeometry(Geometry geom) throws IOException {
1068: MultiLineString ml = (MultiLineString) geom;
1069:
1070: for (int i = 0; i < ml.getNumGeometries(); i++) {
1071: super .writeGeometry(ml.getGeometryN(i));
1072: }
1073: }
1074: }
1075:
1076: /**
1077: *
1078: */
1079: private class PolygonWriter extends SVGFeatureWriter {
1080: /**
1081: * Creates a new PolygonWriter object.
1082: */
1083: public PolygonWriter() {
1084: }
1085:
1086: /**
1087: * DOCUMENT ME!
1088: *
1089: * @throws IOException DOCUMENT ME!
1090: */
1091: protected void startElement(Feature feature) throws IOException {
1092: write("<path");
1093: }
1094:
1095: /**
1096: * DOCUMENT ME!
1097: *
1098: * @throws IOException DOCUMENT ME!
1099: */
1100: protected void startGeometry(Geometry geom) throws IOException {
1101: write(" d=\"");
1102: }
1103:
1104: /**
1105: * DOCUMENT ME!
1106: *
1107: * @param geom DOCUMENT ME!
1108: *
1109: * @throws IOException DOCUMENT ME!
1110: */
1111: protected void writeGeometry(Geometry geom) throws IOException {
1112: Polygon poly = (Polygon) geom;
1113: LineString shell = poly.getExteriorRing();
1114: int nHoles = poly.getNumInteriorRing();
1115: writeClosedPathContent(shell.getCoordinates());
1116:
1117: for (int i = 0; i < nHoles; i++)
1118: writeClosedPathContent(poly.getInteriorRingN(i)
1119: .getCoordinates());
1120: }
1121: }
1122:
1123: /**
1124: *
1125: */
1126: private class MultiPolygonWriter extends PolygonWriter {
1127: /**
1128: * Creates a new MultiPolygonWriter object.
1129: */
1130: public MultiPolygonWriter() {
1131: }
1132:
1133: /**
1134: * DOCUMENT ME!
1135: *
1136: * @param geom DOCUMENT ME!
1137: *
1138: * @throws IOException DOCUMENT ME!
1139: */
1140: protected void writeGeometry(Geometry geom) throws IOException {
1141: MultiPolygon mpoly = (MultiPolygon) geom;
1142:
1143: for (int i = 0; i < mpoly.getNumGeometries(); i++) {
1144: super.writeGeometry(mpoly.getGeometryN(i));
1145: }
1146: }
1147: }
1148: }
|