/** * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * @author Arne Kepp, The Open Planning Project, Copyright 2008 */ package org.geowebcache.service.kml; import java.io.IOException; import java.io.OutputStream; import java.util.LinkedList; import java.util.List; import java.util.Set; import org.geowebcache.GeoWebCacheException; import org.geowebcache.conveyor.ConveyorKMLTile; import org.geowebcache.grid.GridSetBroker; import org.geowebcache.grid.GridSubset; import org.geowebcache.layer.TileLayer; import org.geowebcache.layer.TileLayerDispatcher; import org.geowebcache.mime.MimeType; import org.geowebcache.mime.XMLMime; import org.geowebcache.storage.StorageBroker; public class KMLSiteMap { private ConveyorKMLTile tile = null; private TileLayerDispatcher tLD = null; private StorageBroker storageBroker; private GridSetBroker gridSetBroker; public KMLSiteMap(ConveyorKMLTile tile, TileLayerDispatcher tLD, GridSetBroker gridSetBroker) { this.tile = tile; this.tLD = tLD; this.storageBroker = tile.getStorageBroker(); this.gridSetBroker = gridSetBroker; } public void write() throws GeoWebCacheException, IOException { tile.servletResp.setCharacterEncoding("utf-8"); tile.servletResp.setContentType("application/xml"); tile.servletResp.setStatus(200); if(tile.getHint() == KMLService.HINT_SITEMAP_LAYER) { writeSiteMap(); } else { writeSiteMapIndex(); } } private void writeSiteMapIndex() throws IOException { OutputStream os = tile.servletResp.getOutputStream(); String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<sitemapindex xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n"; os.write(header.getBytes()); writeSiteMapIndexLoop(); String footer = "</sitemapindex>"; os.write(footer.getBytes()); } private void writeSiteMapIndexLoop() throws IOException { OutputStream os = tile.servletResp.getOutputStream(); String urlPrefix = tile.getUrlPrefix(); Iterable<TileLayer> iter = tLD.getLayerList(); for (TileLayer tl : iter) { if (!tl.isEnabled()) { continue; } Set<String> grids = tl.getGridSubsets(); List<MimeType> mimeTypes = tl.getMimeTypes(); if( grids != null && grids.contains(gridSetBroker.WORLD_EPSG4326.getName()) && mimeTypes != null && mimeTypes.contains(XMLMime.kml) ) { String smStr = "<sitemap><loc>"+urlPrefix+tl.getName()+"/sitemap.xml</loc></sitemap>"; os.write(smStr.getBytes()); } } } private void writeSiteMap() throws GeoWebCacheException, IOException { TileLayer layer = tile.getLayer(); GridSubset gridSubset = layer.getGridSubset(gridSetBroker.WORLD_EPSG4326.getName()); writeSiteMapHeader(); long[] gridRect = gridSubset.getCoverageBestFit(); // Check whether we need two tiles for world bounds or not if(gridRect[4] > 0 && (gridRect[2] != gridRect[0] || gridRect[3] != gridRect[1])) { throw new GeoWebCacheException( layer.getName() + " is too big for the sub grid set for " + gridSubset.getName() + ", allow for smaller zoom levels."); } else if(gridRect[0] != gridRect[2]) { long[] gridLocWest = {0,0,0}; long[] gridLocEast = {1,0,0}; writeSiteMapLoop(gridLocWest); writeSiteMapLoop(gridLocEast); } else { long[] gridLoc = {gridRect[0], gridRect[1], gridRect[4]}; writeSiteMapLoop(gridLoc); } writeSiteMapFooter(); } private void writeSiteMapHeader() throws IOException { String header = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\" xmlns:geo=\"http://www.google.com/geo/schemas/sitemap/1.0\">\n"; tile.servletResp.getOutputStream().write(header.getBytes()); } private void writeSiteMapFooter() throws IOException { String footer = "</urlset>"; tile.servletResp.getOutputStream().write(footer.getBytes()); } public void writeSiteMapLoop(long[] gridLoc) throws GeoWebCacheException, IOException { OutputStream os = tile.servletResp.getOutputStream(); TileLayer tileLayer = tile.getLayer(); String urlPrefix = tile.getUrlPrefix(); // Add a link to the super overlay first String superOverlayLoc = "<url><loc>" + urlPrefix.substring(0, urlPrefix.length() - 1) + ".kml.kml</loc><geo:geo><geo:format>kml</geo:format></geo:geo></url>\n"; os.write(superOverlayLoc.getBytes()); LinkedList<long[]> subTileList = new LinkedList<long[]>(); subTileList.addFirst(gridLoc); while(subTileList.peek() != null) { String gridSetId = gridSetBroker.WORLD_EPSG4326.getName(); long[] curLoc = subTileList.removeFirst(); long[][] linkGridLocs = tileLayer.getGridSubset(gridSetId).getSubGrid(curLoc); linkGridLocs = KMZHelper.filterGridLocs(storageBroker, tileLayer, gridSetId, XMLMime.kml, linkGridLocs); // Save the links we still need to follow for later for(long[] subTile : linkGridLocs) { if(subTile[2] > 0) { subTileList.addLast(subTile); } } // We need to link to the data tiles only, for now String tmp = "<url><loc>" + urlPrefix + KMLService.gridLocString(curLoc) +".kml" + "</loc><geo:geo><geo:format>kml</geo:format></geo:geo></url>\n"; os.write(tmp.getBytes()); // Could add priority as 1 / (zoomlevel + 1) } } }