/* * Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, availible at the root * application directory. */ package org.geoserver.wfs.response; import java.io.Writer; import java.util.Calendar; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.util.Converters; import net.sf.json.JSONException; import net.sf.json.util.JSONBuilder; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateSequence; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.impl.CoordinateArraySequence; /** * This class extends the JSONBuilder to be able to write out geometric types. It is coded * against the draft 5 version of the spec on http://geojson.org * * @author Chris Holmes, The Open Planning Project * @version $Id$ * */ public class GeoJSONBuilder extends JSONBuilder { private final Logger LOGGER = org.geotools.util.logging.Logging .getLogger(this.getClass().toString()); public GeoJSONBuilder(Writer w) { super(w); } /** * Writes any geometry object. This class figures out which geometry representation to write * and calls subclasses to actually write the object. * @param geometry The geoemtry be encoded * @return The JSONBuilder with the new geoemtry * @throws JSONException If anything goes wrong */ public JSONBuilder writeGeom(Geometry geometry) throws JSONException { this.object(); this.key("type"); this.value(getGeometryName(geometry)); final int geometryType = getGeometryType(geometry); if (geometryType != MULTIGEOMETRY) { this.key("coordinates"); switch (geometryType) { case POINT: Point point = (Point)geometry; writeCoordinate(point.getX(), point.getY()); break; case LINESTRING: writeCoordinates(((LineString)geometry).getCoordinateSequence()); break; case MULTIPOINT: writeCoordinates(geometry.getCoordinates()); break; case POLYGON: writePolygon((Polygon) geometry); break; case MULTILINESTRING: this.array(); for (int i = 0, n = geometry.getNumGeometries(); i < n; i++) { writeCoordinates(((LineString)geometry.getGeometryN(i)).getCoordinateSequence()); } this.endArray(); break; case MULTIPOLYGON: this.array(); for (int i = 0, n = geometry.getNumGeometries(); i < n; i++) { writePolygon((Polygon) geometry.getGeometryN(i)); } this.endArray(); break; } } else { writeGeomCollection((GeometryCollection) geometry); } return this.endObject(); } private JSONBuilder writeGeomCollection(GeometryCollection collection) { this.array(); this.key("geometries"); for (int i = 0, n = collection.getNumGeometries(); i < n; i++) { writeGeom(collection.getGeometryN(i)); } return this.endArray(); } private JSONBuilder writeCoordinates(Coordinate[] coords) throws JSONException { return writeCoordinates(new CoordinateArraySequence(coords)); } /** * Write the coordinates of a geometry * @param coords The coordinates to write * @return this * @throws JSONException */ private JSONBuilder writeCoordinates(CoordinateSequence coords) throws JSONException { this.array(); final int coordCount = coords.size(); for (int i = 0; i < coordCount; i++) { writeCoordinate(coords.getX(i), coords.getY(i)); } return this.endArray(); } private JSONBuilder writeCoordinate(double x, double y) { this.array(); this.value(x); this.value(y); return this.endArray(); } /** * Turns an envelope into an array [minX,minY,maxX,maxY] * @param env envelope representing bounding box * @return this */ protected JSONBuilder writeBoundingBox(Envelope env) { this.key("bbox"); this.array(); this.value(env.getMinX()); this.value(env.getMinY()); this.value(env.getMaxX()); this.value(env.getMaxY()); return this.endArray(); } /** * Writes a polygon * @param geometry The polygon to write * @throws JSONException */ private void writePolygon(Polygon geometry) throws JSONException { this.array(); writeCoordinates(geometry.getExteriorRing().getCoordinateSequence()); for (int i = 0, ii = geometry.getNumInteriorRing(); i < ii; i++) { writeCoordinates(geometry.getInteriorRingN(i).getCoordinateSequence()); } this.endArray(); //end the linear ring //this.endObject(); //end the } /** Internal representation of OGC SF Point */ protected static final int POINT = 1; /** Internal representation of OGC SF LineString */ protected static final int LINESTRING = 2; /** Internal representation of OGC SF Polygon */ protected static final int POLYGON = 3; /** Internal representation of OGC SF MultiPoint */ protected static final int MULTIPOINT = 4; /** Internal representation of OGC SF MultiLineString */ protected static final int MULTILINESTRING = 5; /** Internal representation of OGC SF MultiPolygon */ protected static final int MULTIPOLYGON = 6; /** Internal representation of OGC SF MultiGeometry */ protected static final int MULTIGEOMETRY = 7; public static String getGeometryName(Geometry geometry) { if (geometry instanceof Point) { return "Point"; } else if (geometry instanceof LineString) { return "LineString"; } else if (geometry instanceof Polygon) { return "Polygon"; } else if (geometry instanceof MultiPoint) { return "MultiPoint"; } else if (geometry instanceof MultiLineString) { return "MultiLineString"; } else if (geometry instanceof MultiPolygon) { return "MultiPolygon"; } else if (geometry instanceof GeometryCollection) { return "GeometryCollection"; } else { throw new IllegalArgumentException("Unknown geometry type " + geometry.getClass()); } } /** * Gets the internal representation for the given Geometry * * @param geometry a Geometry * * @return int representation of Geometry */ public static int getGeometryType(Geometry geometry) { //LOGGER.entering("GMLUtils", "getGeometryType", geometry); if (geometry instanceof Point) { //LOGGER.finest("found point"); return POINT; } else if (geometry instanceof LineString) { //LOGGER.finest("found linestring"); return LINESTRING; } else if (geometry instanceof Polygon) { //LOGGER.finest("found polygon"); return POLYGON; } else if (geometry instanceof MultiPoint) { //LOGGER.finest("found multiPoint"); return MULTIPOINT; } else if (geometry instanceof MultiLineString) { return MULTILINESTRING; } else if (geometry instanceof MultiPolygon) { return MULTIPOLYGON; } else if (geometry instanceof GeometryCollection) { return MULTIGEOMETRY; } else { throw new IllegalArgumentException( "Unable to determine geometry type " + geometry.getClass()); } } /** * Overrides to handle the case of encoding {@code java.util.Date} and its date/time/timestamp * descendants, as well as {@code java.util.Calendar} instances as ISO 8601 strings. * * @see net.sf.json.util.JSONBuilder#value(java.lang.Object) */ @Override public GeoJSONBuilder value(Object value) { if (value instanceof java.util.Date || value instanceof Calendar) { value = Converters.convert(value, String.class); } super.value(value); return this; } }