/* 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.kml;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import org.geoserver.platform.ServiceException;
import org.geoserver.wms.WMS;
import org.geoserver.wms.WMSMapContent;
import org.geoserver.wms.WMSRequests;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.map.Layer;
import org.geotools.referencing.CRS;
import org.geotools.referencing.crs.DefaultGeographicCRS;
import org.geotools.styling.FeatureTypeStyle;
import org.geotools.styling.Symbolizer;
import org.geotools.xml.transform.Translator;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.xml.sax.ContentHandler;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.MultiPolygon;
import com.vividsolutions.jts.geom.Polygon;
/**
* Tranforms a feature colleciton to a kml "Document" element which contains a "Folder" element
* consisting of "GroundOverlay" elements.
* <p>
* Usage:
*
* <pre>
* <code>
* //have a reference to a map context and output stream
* WMSMapContext context = ...
* OutputStream output = ...;
*
* KMLRasterTransformer tx = new KMLRasterTransformer( context );
* for ( int i = 0; i < context.getLayerCount(); i++ ) {
* Layer layer = context.getMapLayer( i );
*
* //transform
* tx.transform( layer, output );
* }
* </code>
* </pre>
*
* </p>
* <p>
* The inline parameter {@link #setInline(boolean)} controls wether the images for the request are
* refernces "inline" as local images, or remoteley as wms requests.
* </p>
*
* @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
*
*/
public class KMLRasterTransformer extends KMLMapTransformer {
/**
* Flag controlling wether images are refernces inline or as remote wms calls.
*/
boolean inline = false;
private KMLLookAt lookAtOpts;
public KMLRasterTransformer(WMS wms, WMSMapContent mapContent) {
this(wms, mapContent, null);
}
public KMLRasterTransformer(WMS wms, WMSMapContent mapContent, KMLLookAt lookAtOpts) {
super(wms, mapContent, null);
this.lookAtOpts = lookAtOpts;
setNamespaceDeclarationEnabled(false);
}
public void setInline(boolean inline) {
this.inline = inline;
}
public Translator createTranslator(ContentHandler handler) {
return new KMLRasterTranslator(handler);
}
class KMLRasterTranslator extends KMLMapTranslatorSupport {
public KMLRasterTranslator(ContentHandler handler) {
super(handler);
}
public void encode(Object o) throws IllegalArgumentException {
Layer layer = (Layer) o;
int mapLayerOrder = mapContent.layers().indexOf(layer);
if (isStandAlone()) {
start("kml");
}
//get the lat lon bbox
ReferencedEnvelope box = new ReferencedEnvelope(mapContent.getRenderingArea());
boolean reprojectBBox = (box.getCoordinateReferenceSystem() != null)
&& !CRS.equalsIgnoreMetadata(box.getCoordinateReferenceSystem(),
DefaultGeographicCRS.WGS84);
if (reprojectBBox) {
try {
box = box.transform(DefaultGeographicCRS.WGS84, true);
} catch (Exception e) {
throw new ServiceException("Could not transform bbox to WGS84", e,
"ReprojectionError", "");
}
}
// start("Document");
// element("name", Layer.getTitle());
// start the folder naming it 'layer_<mapLayerOrder>', this is
// necessary for a GroundOverlay
start("Folder");
element("name", "layer_" + mapLayerOrder);
element("description", layer.getTitle());
if (lookAtOpts != null) {
if (box != null) {
KMLLookAtTransformer tx;
tx = new KMLLookAtTransformer(box, getIndentation(), getEncoding());
Translator translator = tx.createTranslator(contentHandler);
translator.encode(lookAtOpts);
}
}
start("GroundOverlay");
// element( "name", feature.getID() );
element("name", layer.getTitle());
element("drawOrder", Integer.toString(mapLayerOrder));
// encode the icon
start("Icon");
encodeHref(layer);
element("viewRefreshMode", "never");
element("viewBoundScale", "0.75");
end("Icon");
// encde the bounding box
start("LatLonBox");
element("north", Double.toString(box.getMaxY()));
element("south", Double.toString(box.getMinY()));
element("east", Double.toString(box.getMaxX()));
element("west", Double.toString(box.getMinX()));
end("LatLonBox");
end("GroundOverlay");
// if the kmplacemark format option is true, add placemarks to the output
boolean kmplacemark = KMLUtils.getKmplacemark(mapContent.getRequest(), wms);
if (kmplacemark) {
SimpleFeatureCollection features = null;
try {
features = KMLUtils.loadFeatureCollection(
(SimpleFeatureSource) layer.getFeatureSource(), layer,
mapContent, wms, scaleDenominator);
} catch (Exception ex) {
String msg = "Error getting features.";
LOGGER.log(Level.WARNING, msg, ex);
}
if (features != null && features.size() > 0) {
Geometry geom = null;
Geometry centroidGeom = null;
// get geometry of the area of interest
Envelope aoi = mapContent.getRenderingArea();
GeometryFactory factory = new GeometryFactory();
Geometry displayGeom = factory.toGeometry(new Envelope(aoi.getMinX(), aoi
.getMaxX(), aoi.getMinY(), aoi.getMaxY()));
// get the styles for this feature
SimpleFeatureType featureType = features.getSchema();
FeatureTypeStyle[] fts = KMLUtils.filterFeatureTypeStyles(layer.getStyle(),
featureType);
Iterator<SimpleFeature> iter = features.iterator();
while (iter.hasNext()) {
SimpleFeature ftr = iter.next();
geom = (Geometry) ftr.getDefaultGeometry();
List<Symbolizer> symbolizers = filterSymbolizers(ftr, fts);
if (symbolizers.size() != 0)
encodeStyle(ftr, symbolizers);
// if this is a multipolygon, get the largest polygon
// that intersects the AOI
if (geom instanceof MultiPolygon) {
double maxSize = -1;
int numGeoms = geom.getNumGeometries();
for (int i = 0; i < numGeoms; i++) {
Polygon poly = (Polygon) geom.getGeometryN(i);
if (poly.getArea() > maxSize) {
if (displayGeom.intersects(poly)) {
geom = poly;
maxSize = poly.getArea();
}
}
}
}
Geometry g1 = displayGeom.intersection(geom);
// skip if the geometry is not in the AOI
if (g1.isEmpty())
continue;
centroidGeom = g1.getCentroid();
encodePlacemark(ftr, symbolizers, centroidGeom, lookAtOpts);
}
}
}
end("Folder");
// end("Document");
if (isStandAlone()) {
end("kml");
}
}
protected void encodeHref(Layer layer) {
if (inline) {
// inline means reference the image "inline" as in kmz
// use the mapLayerOrder
int mapLayerOrder = mapContent.layers().indexOf(layer);
element("href", "images/layer_" + mapLayerOrder + ".png");
} else {
// reference the image as a remote wms call
element("href",
WMSRequests.getGetMapUrl(mapContent.getRequest(), layer, 0,
mapContent.getRenderingArea(), new String[] { "format",
"image/png", "transparent", "true" }));
}
}
}
}