001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.vfny.geoserver.wms.responses.map.svg;
006:
007: import com.vividsolutions.jts.geom.Envelope;
008: import com.vividsolutions.jts.geom.MultiPoint;
009: import com.vividsolutions.jts.geom.Point;
010: import org.geotools.data.DefaultQuery;
011: import org.geotools.data.FeatureReader;
012: import org.geotools.data.FeatureSource;
013: import org.geotools.data.Query;
014: import org.geotools.feature.FeatureIterator;
015: import org.geotools.feature.FeatureType;
016: import org.geotools.feature.GeometryAttributeType;
017: import org.geotools.filter.Expression;
018: import org.geotools.filter.FilterFactory;
019: import org.geotools.filter.FilterFactoryFinder;
020: import org.geotools.filter.FilterType;
021: import org.geotools.filter.GeometryFilter;
022: import org.geotools.map.MapLayer;
023: import org.vfny.geoserver.wms.WMSMapContext;
024: import java.io.IOException;
025: import java.io.OutputStream;
026: import java.util.logging.Logger;
027:
028: /**
029: * DOCUMENT ME!
030: *
031: * @author Gabriel Rold?n
032: * @version $Id: EncodeSVG.java 7746 2007-11-13 15:38:35Z aaime $
033: */
034: public class EncodeSVG {
035: /** DOCUMENT ME! */
036: private static final Logger LOGGER = org.geotools.util.logging.Logging
037: .getLogger("org.vfny.geoserver.responses.wms.map");
038: private static final String DOCTYPE = "<!DOCTYPE svg \n\tPUBLIC \"-//W3C//DTD SVG 20001102//EN\" \n\t\"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd\">\n";
039:
040: /** the XML and SVG header */
041: private static final String SVG_HEADER = "<?xml version=\"1.0\" standalone=\"no\"?>\n\t"
042: + "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" \n\tstroke=\"green\" \n\tfill=\"none\" \n\tstroke-width=\"0.1%\"\n\tstroke-linecap=\"round\"\n\tstroke-linejoin=\"round\"\n\twidth=\"_width_\" \n\theight=\"_height_\" \n\tviewBox=\"_viewBox_\" \n\tpreserveAspectRatio=\"xMidYMid meet\">\n";
043:
044: /** the SVG closing element */
045: private static final String SVG_FOOTER = "</svg>\n";
046:
047: /** DOCUMENT ME! */
048: private WMSMapContext mapContext;
049:
050: /** DOCUMENT ME! */
051: private SVGWriter writer;
052:
053: /** DOCUMENT ME! */
054: private boolean abortProcess;
055:
056: /**
057: * Creates a new EncodeSVG object.
058: *
059: * @param mapContext DOCUMENT ME!
060: */
061: public EncodeSVG(WMSMapContext mapContext) {
062: this .mapContext = mapContext;
063: }
064:
065: /**
066: * DOCUMENT ME!
067: */
068: public void abort() {
069: abortProcess = true;
070: }
071:
072: /**
073: * DOCUMENT ME!
074: *
075: * @param out DOCUMENT ME!
076: *
077: * @throws IOException DOCUMENT ME!
078: */
079: public void encode(final OutputStream out) throws IOException {
080: Envelope env = this .mapContext.getAreaOfInterest();
081: this .writer = new SVGWriter(out, mapContext);
082: writer.setMinCoordDistance(env.getWidth() / 1000);
083:
084: abortProcess = false;
085:
086: long t = System.currentTimeMillis();
087:
088: try {
089: writeHeader();
090:
091: writeLayers();
092:
093: writer.write(SVG_FOOTER);
094:
095: this .writer.flush();
096: t = System.currentTimeMillis() - t;
097: LOGGER.info("SVG generated in " + t + " ms");
098: } catch (IOException ioe) {
099: if (abortProcess) {
100: LOGGER.fine("SVG encoding aborted");
101:
102: return;
103: } else {
104: throw ioe;
105: }
106: } catch (AbortedException ex) {
107: return;
108: }
109: }
110:
111: /**
112: * DOCUMENT ME!
113: *
114: * @return DOCUMENT ME!
115: */
116: public String createViewBox() {
117: Envelope referenceSpace = mapContext.getAreaOfInterest();
118: String viewBox = writer.getX(referenceSpace.getMinX())
119: + " "
120: + (writer.getY(referenceSpace.getMinY()) - referenceSpace
121: .getHeight()) + " " + referenceSpace.getWidth()
122: + " " + referenceSpace.getHeight();
123:
124: return viewBox;
125: }
126:
127: /**
128: * DOCUMENT ME!
129: *
130: * @throws IOException DOCUMENT ME!
131: */
132: private void writeHeader() throws IOException {
133: //TODO: this does not write out the doctype definition, there should be
134: // a configuration option wether to include it or not.
135: String viewBox = createViewBox();
136: String header = SVG_HEADER.replaceAll("_viewBox_", viewBox);
137: header = header.replaceAll("_width_", String.valueOf(mapContext
138: .getMapWidth()));
139: header = header.replaceAll("_height_", String
140: .valueOf(mapContext.getMapHeight()));
141: writer.write(header);
142: }
143:
144: /**
145: * DOCUMENT ME!
146: *
147: * @param layer DOCUMENT ME!
148: *
149: * @throws IOException DOCUMENT ME!
150: */
151: private void writeDefs(FeatureType layer) throws IOException {
152: GeometryAttributeType gtype = layer.getDefaultGeometry();
153: Class geometryClass = gtype.getType();
154:
155: if ((geometryClass == MultiPoint.class)
156: || (geometryClass == Point.class)) {
157: writePointDefs();
158: }
159: }
160:
161: /**
162: * DOCUMENT ME!
163: *
164: * @throws IOException DOCUMENT ME!
165: */
166: private void writePointDefs() throws IOException {
167: writer
168: .write("<defs>\n\t<circle id='point' cx='0' cy='0' r='0.25%' fill='blue'/>\n</defs>\n");
169: }
170:
171: /**
172: * DOCUMENT ME!
173: *
174: * @throws IOException DOCUMENT ME!
175: * @throws AbortedException DOCUMENT ME!
176: *
177: * @task TODO: respect layer filtering given by their Styles
178: */
179: private void writeLayers() throws IOException, AbortedException {
180: MapLayer[] layers = mapContext.getLayers();
181: int nLayers = layers.length;
182:
183: // FeatureTypeInfo layerInfo = null;
184: int defMaxDecimals = writer.getMaximunFractionDigits();
185:
186: FilterFactory fFac = FilterFactoryFinder.createFilterFactory();
187:
188: for (int i = 0; i < nLayers; i++) {
189: MapLayer layer = layers[i];
190: FeatureIterator featureReader = null;
191: FeatureSource fSource = layer.getFeatureSource();
192: FeatureType schema = fSource.getSchema();
193:
194: try {
195: Expression bboxExpression = fFac
196: .createBBoxExpression(mapContext
197: .getAreaOfInterest());
198: GeometryFilter bboxFilter = fFac
199: .createGeometryFilter(FilterType.GEOMETRY_INTERSECTS);
200: bboxFilter.addLeftGeometry(fFac
201: .createAttributeExpression(schema, schema
202: .getDefaultGeometry().getName()));
203: bboxFilter.addRightGeometry(bboxExpression);
204:
205: Query bboxQuery = new DefaultQuery(
206: schema.getTypeName(), bboxFilter);
207:
208: LOGGER.fine("obtaining FeatureReader for "
209: + schema.getTypeName());
210: featureReader = fSource.getFeatures(bboxQuery)
211: .features();
212: LOGGER.fine("got FeatureReader, now writing");
213:
214: String groupId = null;
215: String styleName = null;
216:
217: groupId = schema.getTypeName();
218:
219: styleName = layer.getStyle().getName();
220:
221: writer.write("<g id=\"" + groupId + "\"");
222:
223: if (!styleName.startsWith("#")) {
224: writer.write(" class=\"" + styleName + "\"");
225: }
226:
227: writer.write(">\n");
228:
229: writeDefs(schema);
230:
231: writer.writeFeatures(fSource.getSchema(),
232: featureReader, styleName);
233: writer.write("</g>\n");
234: } catch (IOException ex) {
235: throw ex;
236: } catch (AbortedException ae) {
237: LOGGER.info("process aborted: " + ae.getMessage());
238: throw ae;
239: } catch (Throwable t) {
240: LOGGER.warning("UNCAUGHT exception: " + t.getMessage());
241:
242: IOException ioe = new IOException(
243: "UNCAUGHT exception: " + t.getMessage());
244: ioe.setStackTrace(t.getStackTrace());
245: throw ioe;
246: } finally {
247: if (featureReader != null) {
248: featureReader.close();
249: }
250: }
251: }
252: }
253: }
|