/* 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.vfny.geoserver.wms.responses.map.kml; import java.io.IOException; import java.io.OutputStream; import java.nio.charset.Charset; import java.util.logging.Logger; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; import javax.xml.transform.TransformerException; import org.geoserver.platform.ServiceException; import org.geoserver.wms.WMS; import org.geotools.map.MapLayer; import org.vfny.geoserver.wms.GetMapProducer; import org.vfny.geoserver.wms.WMSMapContext; import org.vfny.geoserver.wms.WmsException; import org.vfny.geoserver.wms.requests.GetMapRequest; import org.vfny.geoserver.wms.responses.AbstractGetMapProducer; import org.vfny.geoserver.wms.responses.map.png.PNGMapProducer; /** * Handles a GetMap request that spects a map in KMZ format. * * KMZ files are a zipped KML file. The KML file must have an emcompasing * <document> or <folder> element. So if you have many different placemarks or * ground overlays, they all need to be contained within one <document> element, * then zipped up and sent off with the extension "kmz". * * @author $Author: Alessio Fabiani (alessio.fabiani@gmail.com) $ * @author $Author: Simone Giannecchini (simboss1@gmail.com) $ * @author $Author: Brent Owens * @author Justin Deoliveira * */ public class KMZMapProducer extends AbstractGetMapProducer implements GetMapProducer { /** standard logger */ private static final Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.vfny.geoserver.responses.wms.kmz"); /** * Official KMZ mime type */ static final String MIME_TYPE = "application/vnd.google-earth.kmz+xml"; public static final String[] OUTPUT_FORMATS = { MIME_TYPE, "application/vnd.google-earth.kmz", "kmz", "application/vnd.google-earth.kmz xml" }; /** * delegating producer for rendering. */ PNGMapProducer mapProducer; /** * transformer for creating kml */ KMLTransformer transformer; public KMZMapProducer(WMS wms) { super(MIME_TYPE, OUTPUT_FORMATS); mapProducer = new PNGMapProducer(wms); } public void abort() { LOGGER.fine("aborting KMZ map response"); mapContext = null; mapProducer = null; transformer = null; } public String getContentDisposition() { return super.getContentDisposition(".kmz"); } public String getContentType() throws IllegalStateException { return MIME_TYPE; } /** * Initializes the KML encoder. None of the map production is done here, it * is done in writeTo(). This way the output can be streamed directly to the * output response and not written to disk first, then loaded in and then * sent to the response. * * @param map * WMSMapContext describing what layers, styles, area of interest * etc are to be used when producing the map. * * @throws WmsException * thrown if anything goes wrong during the production. */ public void produceMap() throws WmsException { transformer = new KMLTransformer(); transformer.setKmz(true); GetMapRequest request = mapContext.getRequest(); WMS wms = request.getWMS(); Charset encoding = wms.getCharSet(); transformer.setEncoding(encoding); // TODO: use GeoServer.isVerbose() to determine if we should indent? transformer.setIndentation(3); } /** * Makes the map and sends it to the zipped output stream The produceMap() * method does not create the map in this case. We produce the map here so * we can stream directly to the response output stream, and not have to * write to disk, then send it to the stream. * * @Note: Do not close the output stream in this method, it gets closed * later on. * * @param out * OutputStream to stream the map to. * * @throws ServiceException * @throws IOException * */ public void writeTo(OutputStream out) throws ServiceException, IOException { // wrap the output stream in a zipped one ZipOutputStream zip = new ZipOutputStream(out); // first create an entry for the kml ZipEntry entry = new ZipEntry("wms.kml"); zip.putNextEntry(entry); try { transformer.transform(mapContext, zip); zip.closeEntry(); } catch (TransformerException e) { throw (IOException) new IOException().initCause(e); } // write the images for (int i = 0; i < mapContext.getLayerCount(); i++) { MapLayer mapLayer = mapContext.getLayer(i); // create a context for this single layer WMSMapContext mapContext = new WMSMapContext(); mapContext.addLayer(mapLayer); mapContext.setRequest(this.mapContext.getRequest()); mapContext.setMapHeight(this.mapContext.getMapHeight()); mapContext.setMapWidth(this.mapContext.getMapWidth()); mapContext.setAreaOfInterest(this.mapContext.getAreaOfInterest()); mapContext.setBgColor(this.mapContext.getBgColor()); mapContext.setBuffer(this.mapContext.getBuffer()); mapContext.setContactInformation(this.mapContext .getContactInformation()); mapContext.setKeywords(this.mapContext.getKeywords()); mapContext.setAbstract(this.mapContext.getAbstract()); mapContext.setTransparent(true); // render the map mapProducer.setMapContext(mapContext); mapProducer.produceMap(); // write it to the zip stream entry = new ZipEntry("layer_" + i + ".png"); zip.putNextEntry(entry); mapProducer.writeTo(zip); zip.closeEntry(); } zip.finish(); zip.flush(); } }