package org.geoserver.kml.builder; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Logger; import org.apache.commons.lang.ObjectUtils; import org.apache.commons.lang.SystemUtils; import org.geoserver.catalog.LayerInfo; import org.geoserver.kml.KmlEncodingContext; import org.geoserver.kml.decorator.LookAtDecoratorFactory; import org.geoserver.kml.utils.LookAtOptions; import org.geoserver.wms.GetMapRequest; import org.geoserver.wms.MapLayerInfo; import org.geoserver.wms.WMSMapContent; import org.geoserver.wms.WMSRequests; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.styling.Style; import org.geotools.util.logging.Logging; import com.vividsolutions.jts.geom.Envelope; import de.micromata.opengis.kml.v_2_2_0.Document; import de.micromata.opengis.kml.v_2_2_0.Link; import de.micromata.opengis.kml.v_2_2_0.LookAt; import de.micromata.opengis.kml.v_2_2_0.NetworkLink; import de.micromata.opengis.kml.v_2_2_0.RefreshMode; import de.micromata.opengis.kml.v_2_2_0.ViewRefreshMode; /** * Builds a KML document that has a network link for each layer, no superoverlays involved * * @author Andrea Aime - GeoSolutions * */ public class SimpleNetworkLinkBuilder extends AbstractNetworkLinkBuilder { static final Logger LOGGER = Logging.getLogger(SimpleNetworkLinkBuilder.class); static final String REFRESH_KEY = "kmlrefresh"; static final String VISIBLE_KEY = "kmlvisible"; public SimpleNetworkLinkBuilder(KmlEncodingContext context) { super(context); } @Override void encodeDocumentContents(Document container) { WMSMapContent mapContent = context.getMapContent(); GetMapRequest request = context.getRequest(); Map formatOptions = request.getFormatOptions(); LookAtDecoratorFactory lookAtFactory = new LookAtDecoratorFactory(); LookAtOptions lookAtOptions = new LookAtOptions(formatOptions); // compute the layer bounds and the total bounds List<ReferencedEnvelope> layerBounds = new ArrayList<ReferencedEnvelope>(mapContent .layers().size()); ReferencedEnvelope aggregatedBounds = computePerLayerQueryBounds(mapContent, layerBounds, null); if (aggregatedBounds != null) { LookAt la = lookAtFactory.buildLookAt(aggregatedBounds, lookAtOptions, false); container.setAbstractView(la); } final List<MapLayerInfo> layers = request.getLayers(); final List<Style> styles = request.getStyles(); for (int i = 0; i < layers.size(); i++) { MapLayerInfo layerInfo = layers.get(i); NetworkLink nl = container.createAndAddNetworkLink(); nl.setName(layerInfo.getLabel()); if (layerInfo.getDescription() != null && layerInfo.getDescription().length() > 0) { nl.setDescription(layerInfo.getDescription()); } // Allow for all layers to be disabled by default. This can be advantageous with // multiple large data-sets. if (formatOptions.get(VISIBLE_KEY) != null) { boolean visible = Boolean.parseBoolean(formatOptions.get(VISIBLE_KEY).toString()); nl.setVisibility(visible); } else { nl.setVisibility(true); } nl.setOpen(true); // look at for this layer Envelope requestBox = context.getRequestBoxWGS84(); if (requestBox != null) { LookAt la = lookAtFactory.buildLookAt(requestBox, lookAtOptions, false); nl.setAbstractView(la); } // set bbox to null so its not included in the request, google // earth will append it for us GetMapRequest requestCopy = (GetMapRequest) request.clone(); requestCopy.setBbox(null); String style = i < styles.size() ? styles.get(i).getName() : null; String href = WMSRequests.getGetMapUrl(requestCopy, layers.get(i).getName(), i, style, null, null); try { // WMSRequests.getGetMapUrl returns a URL encoded query string, but GoogleEarth // 6 doesn't like URL encoded parameters. See GEOS-4483 href = URLDecoder.decode(href, "UTF-8"); } catch (UnsupportedEncodingException e) { throw new RuntimeException(e); } Link url = nl.createAndSetUrl(); url.setHref(href); url.setViewRefreshMode(ViewRefreshMode.ON_STOP); url.setViewRefreshTime(1); url.setViewBoundScale(1); // Attempt to parse a value from kmlrefresh format_options parameter. // It can be either a set interval in seconds or "expires". // "expires" uses the HTTP max-age header and falls-back to expires header // to determine the time when a refresh should occur. if (formatOptions.get(REFRESH_KEY) != null) { String refreshValue = formatOptions.get(REFRESH_KEY).toString(); if (refreshValue.equalsIgnoreCase("expires")) { url.setRefreshMode(RefreshMode.ON_EXPIRE); } else { int interval = Integer.parseInt(refreshValue); url.setRefreshInterval(interval); url.setRefreshMode(RefreshMode.ON_INTERVAL); } } } } }