001: package org.geoserver.wfs.response;
002:
003: import java.io.BufferedWriter;
004: import java.io.IOException;
005: import java.io.OutputStream;
006: import java.io.OutputStreamWriter;
007: import java.io.Writer;
008: import java.util.Iterator;
009: import java.util.List;
010: import java.util.logging.Logger;
011:
012: import net.opengis.wfs.FeatureCollectionType;
013: import net.sf.json.JSONException;
014:
015: import org.geoserver.platform.Operation;
016: import org.geoserver.platform.ServiceException;
017: import org.geoserver.wfs.WFS;
018: import org.geoserver.wfs.WFSGetFeatureOutputFormat;
019: import org.geotools.feature.AttributeType;
020: import org.geotools.feature.Feature;
021: import org.geotools.feature.FeatureCollection;
022: import org.geotools.feature.FeatureIterator;
023: import org.geotools.feature.FeatureType;
024: import org.geotools.geometry.jts.ReferencedEnvelope;
025: import org.geotools.referencing.NamedIdentifier;
026: import org.opengis.referencing.crs.CoordinateReferenceSystem;
027:
028: import com.vividsolutions.jts.geom.Geometry;
029:
030: public class GeoJSONOutputFormat extends WFSGetFeatureOutputFormat {
031: private final Logger LOGGER = org.geotools.util.logging.Logging
032: .getLogger(this .getClass().toString());
033:
034: /**
035: * WFS configuration
036: */
037: private WFS wfs;
038:
039: public static final String FORMAT = "json";
040:
041: public GeoJSONOutputFormat(WFS wfs) {
042: super (FORMAT);
043: this .wfs = wfs;
044: }
045:
046: public String getMimeType(Object value, Operation operation)
047: throws ServiceException {
048: return "application/json";
049: }
050:
051: public String getCapabilitiesElementName() {
052: return "GEOJSON";
053: }
054:
055: protected String getContentDisposition(
056: FeatureCollectionType featureCollection) {
057:
058: StringBuffer sb = new StringBuffer();
059: for (Iterator f = featureCollection.getFeature().iterator(); f
060: .hasNext();) {
061: FeatureCollection fc = (FeatureCollection) f.next();
062: sb.append(fc.getSchema().getTypeName() + "_");
063: }
064: sb.setLength(sb.length() - 1);
065: return "inline; filename=" + sb.toString() + ".txt";
066:
067: }
068:
069: protected void write(FeatureCollectionType featureCollection,
070: OutputStream output, Operation getFeature)
071: throws IOException, ServiceException {
072:
073: //TODO: investigate setting proper charsets in this
074: //it's part of the constructor, just need to hook it up.
075: Writer outWriter = new BufferedWriter(new OutputStreamWriter(
076: output));
077:
078: GeoJSONBuilder jsonWriter = new GeoJSONBuilder(outWriter);
079:
080: // execute should of set all the header information
081: // including the lockID
082: //
083: // execute should also fail if all of the locks could not be aquired
084: List resultsList = featureCollection.getFeature();
085:
086: //FeatureResults[] featureResults = (FeatureResults[]) resultsList
087: // .toArray(new FeatureResults[resultsList.size()]);
088: LOGGER.info("about to encode JSON");
089:
090: // Generate bounds for every feature?
091: boolean featureBounding = wfs.isFeatureBounding();
092: boolean hasGeom = false;
093:
094: try {
095: jsonWriter.object().key("type").value("FeatureCollection");
096: jsonWriter.key("features");
097: jsonWriter.array();
098:
099: CoordinateReferenceSystem crs = null;
100: for (int i = 0; i < resultsList.size(); i++) {
101: FeatureCollection collection = (FeatureCollection) resultsList
102: .get(i);
103: FeatureIterator iterator = collection.features();
104:
105: try {
106: FeatureType fType;
107: AttributeType[] types;
108:
109: while (iterator.hasNext()) {
110: Feature feature = iterator.next();
111: jsonWriter.object();
112: jsonWriter.key("type").value("Feature");
113: jsonWriter.key("id").value(feature.getID());
114:
115: fType = feature.getFeatureType();
116: types = fType.getAttributeTypes();
117:
118: AttributeType defaultGeomType = fType
119: .getDefaultGeometry();
120:
121: if (crs == null && defaultGeomType != null)
122: crs = fType.getDefaultGeometry()
123: .getCoordinateSystem();
124:
125: jsonWriter.key("geometry");
126: Geometry aGeom = feature.getDefaultGeometry();
127:
128: if (aGeom == null) {
129: // In case the default geometry is not set, we will just use the first geometry we find
130: for (int j = 0; j < types.length
131: && aGeom == null; j++) {
132: Object value = feature.getAttribute(j);
133: if (value != null
134: && value instanceof Geometry) {
135: aGeom = (Geometry) value;
136: }
137: }
138: }
139: // Write the geometry, whether it is a null or not
140: if (aGeom != null) {
141: jsonWriter.writeGeom(aGeom);
142: hasGeom = true;
143: } else {
144: jsonWriter.value(null);
145: }
146: if (defaultGeomType != null)
147: jsonWriter.key("geometry_name").value(
148: defaultGeomType.getLocalName());
149:
150: jsonWriter.key("properties");
151: jsonWriter.object();
152:
153: for (int j = 0; j < types.length; j++) {
154: Object value = feature.getAttribute(j);
155:
156: if (value != null) {
157: if (value instanceof Geometry) {
158: //This is an area of the spec where they decided to 'let
159: //convention evolve', that is how to handle multiple
160: //geometries. My take is to print the geometry here if
161: //it's not the default. If it's the default that you already
162: //printed above, so you don't need it here.
163: if (types[j]
164: .equals(defaultGeomType)) {
165: //Do nothing, we wrote it above
166: //jsonWriter.value("geometry_name");
167: } else {
168: jsonWriter.key(types[j]
169: .getLocalName());
170: jsonWriter
171: .writeGeom((Geometry) value);
172: }
173: } else {
174: jsonWriter.key(types[j]
175: .getLocalName());
176: jsonWriter.value(value);
177: }
178:
179: } else {
180: jsonWriter.key(types[j].getLocalName());
181: jsonWriter.value(null);
182: }
183: }
184: // Bounding box for feature in properties
185: ReferencedEnvelope refenv = feature.getBounds();
186: if (featureBounding && !refenv.isEmpty())
187: jsonWriter.writeBoundingBox(refenv);
188:
189: jsonWriter.endObject(); //end the properties
190: jsonWriter.endObject(); //end the feature
191: }
192: } //catch an exception here?
193: finally {
194: collection.close(iterator);
195: }
196:
197: jsonWriter.endArray(); //end features
198:
199: // Coordinate Referense System, currently only if the namespace is EPSG
200: if (crs != null) {
201: NamedIdentifier namedIdent = (NamedIdentifier) crs
202: .getIdentifiers().iterator().next();
203: String csStr = namedIdent.getCodeSpace()
204: .toUpperCase();
205:
206: if (csStr.equals("EPSG")) {
207: jsonWriter.key("crs");
208: jsonWriter.object();
209: jsonWriter.key("type").value(csStr);
210: jsonWriter.key("properties");
211: jsonWriter.object();
212: jsonWriter.key("code");
213: jsonWriter.value(namedIdent.getCode());
214: jsonWriter.endObject(); // end properties
215: jsonWriter.endObject(); // end crs
216: }
217: }
218:
219: // Bounding box for featurecollection
220: if (hasGeom)
221: jsonWriter.writeBoundingBox(collection.getBounds());
222:
223: jsonWriter.endObject(); // end featurecollection
224: outWriter.flush();
225: }
226: } catch (JSONException jsonException) {
227: ServiceException serviceException = new ServiceException(
228: "Error: " + jsonException.getMessage());
229: serviceException.initCause(jsonException);
230: throw serviceException;
231: }
232:
233: }
234:
235: }
|