package org.geoserver.geosearch; import static org.geoserver.ows.util.ResponseUtils.*; import org.geoserver.ows.URLMangler.URLType; import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; import org.jdom.Document; import org.jdom.Element; import org.jdom.Namespace; import org.opengis.feature.simple.SimpleFeature; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import org.restlet.data.MediaType; import org.restlet.data.Method; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.data.Status; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.LineSegment; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.Point; public class KMLFeatureDescription extends AbstractFeatureDescription { private Namespace KML = Namespace.getNamespace("http://www.opengis.net/kml/2.2"); private String GEOSERVER_URL; public void handle(Request req, Response resp) { GEOSERVER_URL = getBaseURL(req); if (req.getMethod().equals(Method.GET)) { doGet(req, resp); } else { resp.setStatus(Status.CLIENT_ERROR_METHOD_NOT_ALLOWED); } } public void doGet(Request req, Response resp) { SimpleFeature f = findFeature(req); String prefix = (String)req.getAttributes().get("namespace"); String type = (String)req.getAttributes().get("layer"); Document kml = buildKMLDoc(prefix + ":" + type, f); resp.setEntity(new JDOMRepresentation(kml, new MediaType("application/vnd.google-earth.kml+xml"))); } private Document buildKMLDoc(String typeName, SimpleFeature f) { Document kml = new Document(); Element root = new Element("kml", KML); Element doc = new Element("Document"); doc.addContent(buildLookAt(f)); doc.addContent(buildNetworkLink(typeName)); root.addContent(doc); kml.setRootElement(root); return kml; } private Element buildLookAt(SimpleFeature f) { Element lookat = new Element("LookAt"); Coordinate p = getLatLonCentroid(f); lookat.addContent(new Element("longitude").addContent("" + p.x)); lookat.addContent(new Element("latitude").addContent("" + p.y)); lookat.addContent(new Element("altitude").addContent("0")); lookat.addContent(new Element("range").addContent("700")); lookat.addContent(new Element("tilt").addContent("0")); lookat.addContent(new Element("heading").addContent("0")); lookat.addContent( new Element("altitudeMode").addContent("clampToGround") ); return lookat; } private Element buildNetworkLink(String typeName) { Element networklink = new Element("NetworkLink"); networklink.addContent(new Element("name").addContent( "Complete " + typeName + " hierarchy")); networklink.addContent(new Element("visibility").addContent("1")); Element link = new Element("Link"); link.addContent(new Element("href").addContent( buildURL(GEOSERVER_URL, "wms/kml?layers=" + typeName, null, URLType.SERVICE))); networklink.addContent(link); return networklink; } private Coordinate getLatLonCentroid(SimpleFeature f) { Coordinate c = geometryCentroid((Geometry)f.getDefaultGeometry()); try { CoordinateReferenceSystem nativeCRS = f.getType().getCoordinateReferenceSystem(); CoordinateReferenceSystem latLon = CRS.decode("EPSG:4326"); if (!CRS.equalsIgnoreMetadata(nativeCRS, latLon)) { MathTransform xform = CRS.findMathTransform(nativeCRS, latLon, true); //convert data bbox to lat/long c = JTS.transform(c, null, xform); } } catch (Exception e) { // TODO: Log error } return c; } /** * Returns the centroid of the geometry, handling a geometry collection. * <p> * In the case of a collection a multi point containing the centroid of * each geometry in the collection is calculated. The first point in the * multi point is returned as the cetnroid. * </p> */ Coordinate geometryCentroid(Geometry g) { // TODO: should the collection case return the centroid of the // multi point? if (g instanceof GeometryCollection) { GeometryCollection gc = (GeometryCollection) g; // check for case of single geometry if (gc.getNumGeometries() == 1) { g = gc.getGeometryN(0); } else { double maxAreaSoFar = gc.getGeometryN(0).getArea(); Coordinate centroidToReturn = gc.getGeometryN(0).getCentroid().getCoordinate(); for (int t = 0; t < gc.getNumGeometries(); t++) { double area = gc.getGeometryN(t).getArea(); if (area > maxAreaSoFar) { maxAreaSoFar = area; centroidToReturn = gc.getGeometryN(t).getCentroid().getCoordinate(); } } return centroidToReturn; } } if (g instanceof Point) { // thats easy return g.getCoordinate(); } else if (g instanceof LineString) { // make sure the point we return is actually on the line double tol = 1E-6; double mid = g.getLength() / 2d; Coordinate[] coords = g.getCoordinates(); // walk along the linestring until we get to a point where we // have two coordinates that straddle the midpoint double len = 0d; for (int i = 1; i < coords.length; i++) { LineSegment line = new LineSegment(coords[i - 1], coords[i]); len += line.getLength(); if (Math.abs(len - mid) < tol) { // close enough return line.getCoordinate(1); } if (len > mid) { // we have gone past midpoint return line.pointAlong(1 - ((len - mid) / line .getLength())); } } // should never get there return g.getCentroid().getCoordinate(); } else { // return the actual centroid return g.getCentroid().getCoordinate(); } } }