/* (c) 2014 - 2016 Open Source Geospatial Foundation - all rights reserved * (c) 2001 - 2013 OpenPlans * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ package org.geoserver.web.demo; import java.io.IOException; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.wicket.request.resource.PackageResourceReference; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerGroupInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.PublishedType; import org.geoserver.ows.URLMangler.URLType; import org.geoserver.ows.util.ResponseUtils; import org.geoserver.web.CatalogIconFactory; import org.geoserver.web.GeoServerApplication; import org.geoserver.wfs.xml.GML32OutputFormat; import org.geoserver.wms.DefaultWebMapService; import org.geoserver.wms.GetMapRequest; import org.geoserver.wms.MapLayerInfo; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.gml2.bindings.GML2EncodingUtils; import org.geotools.util.NullProgressListener; import org.geotools.util.ProgressListener; import org.geotools.util.logging.Logging; import org.opengis.feature.type.AttributeType; import org.opengis.feature.type.FeatureType; import org.opengis.feature.type.Name; import com.google.common.collect.Iterables; import com.vividsolutions.jts.geom.Envelope; import javax.servlet.http.HttpServletRequest; /** * A model class for the UI, hides the difference between simple layers and * groups, centralizes the computation of a valid preview request */ public class PreviewLayer { static final Logger LOGGER = Logging.getLogger(PreviewLayer.class); public enum PreviewLayerType { Raster, Vector, Remote, Group }; LayerInfo layerInfo; LayerGroupInfo groupInfo; transient GetMapRequest request; public PreviewLayer(LayerInfo layerInfo) { this.layerInfo = layerInfo; } public PreviewLayer(LayerGroupInfo groupInfo) { this.groupInfo = groupInfo; } public String getName() { if (layerInfo != null) { return layerInfo.getResource().prefixedName(); } else { return groupInfo.prefixedName(); } } public String getWorkspace() { if (layerInfo != null) { return layerInfo.getResource().getStore().getWorkspace().getName(); } else if (groupInfo != null && groupInfo.getWorkspace() != null){ return groupInfo.getWorkspace().getName(); } return null; } public PackageResourceReference getIcon() { if(layerInfo != null) return CatalogIconFactory.get().getSpecificLayerIcon(layerInfo); else return CatalogIconFactory.GROUP_ICON; } public PackageResourceReference getTypeSpecificIcon() { if(layerInfo != null) return CatalogIconFactory.get().getSpecificLayerIcon(layerInfo); else return CatalogIconFactory.GROUP_ICON; } public String getTitle() { if(layerInfo != null) { return layerInfo.getResource().getTitle(); } else if(groupInfo != null) { return groupInfo.getTitle(); } else { return ""; } } public String getAbstract() { if(layerInfo != null) { return layerInfo.getResource().getAbstract(); } else if(groupInfo != null) { return groupInfo.getAbstract(); } else { return ""; } } public String getKeywords() { if(layerInfo != null) { return layerInfo.getResource().getKeywords().toString(); } else { return ""; } } public PreviewLayer.PreviewLayerType getType() { if (layerInfo != null) { if (layerInfo.getType() == PublishedType.RASTER) return PreviewLayerType.Raster; else if (layerInfo.getType() == PublishedType.VECTOR) return PreviewLayerType.Vector; else return PreviewLayerType.Remote; } else { return PreviewLayerType.Group; } } /** * Builds a fake GetMap request * * @param prefixedName * */ GetMapRequest getRequest() { if (request == null) { GeoServerApplication app = GeoServerApplication.get(); request = new GetMapRequest(); Catalog catalog = app.getCatalog(); List<MapLayerInfo> layers = expandLayers(catalog); request.setLayers(layers); request.setFormat("application/openlayers"); // in the case of groups we already know about the envelope and the target SRS if(groupInfo != null) { ReferencedEnvelope bounds = groupInfo.getBounds(); request.setBbox(bounds); String epsgCode = GML2EncodingUtils.epsgCode(bounds.getCoordinateReferenceSystem()); if(epsgCode != null) request.setSRS("EPSG:" + epsgCode); } try { DefaultWebMapService.autoSetBoundsAndSize(request); } catch (Exception e) { LOGGER.log(Level.INFO, "Could not set figure out automatically a good preview link for " + getName(), e); } } return request; } /** * Expands the specified name into a list of layer info names * * @param name * @param catalog * */ private List<MapLayerInfo> expandLayers(Catalog catalog) { List<MapLayerInfo> layers = new ArrayList<MapLayerInfo>(); if (layerInfo != null) { layers.add(new MapLayerInfo(layerInfo)); } else { for (LayerInfo l : Iterables.filter(groupInfo.getLayers(), LayerInfo.class)) { layers.add(new MapLayerInfo(l)); } } return layers; } String getBaseUrl(String service) { return getBaseUrl(service, false); } String getBaseUrl(String service, boolean useGlobalRef) { HttpServletRequest req = GeoServerApplication.get().servletRequest(); String base = ResponseUtils.baseURL(req); String ws = getWorkspace(); if(ws == null || useGlobalRef) { // global reference return ResponseUtils.buildURL(base, service, null, URLType.SERVICE); } else { return ResponseUtils.buildURL(base, ws + "/" + service, null, URLType.SERVICE); } } /** * Given a request and a target format, builds the WMS request * * @param request * @param string * */ public String getWmsLink() { GetMapRequest request = getRequest(); final Envelope bbox = request.getBbox(); if (bbox == null) return null; return getBaseUrl("wms") + "?service=WMS&version=1.1.0&request=GetMap" // + "&layers=" + getName() // + "&styles=" // + "&bbox=" + bbox.getMinX() + "," + bbox.getMinY() // + "," + bbox.getMaxX() + "," + bbox.getMaxY() // + "&width=" + request.getWidth() // + "&height=" + request.getHeight() + "&srs=" + request.getSRS(); } /** * Returns the default GML link for this layer. * * @param gmlParamsCache optional map where computed GML output params are cached * */ public String getGmlLink(Map<String, GMLOutputParams> gmlParamsCache) { GMLOutputParams gmlParams = new GMLOutputParams(); if (layerInfo != null) { if (layerInfo.getResource() instanceof FeatureTypeInfo) { FeatureTypeInfo ftInfo = (FeatureTypeInfo) layerInfo.getResource(); if (ftInfo.getStore() != null) { Map<String, Serializable> connParams = ftInfo.getStore() .getConnectionParameters(); if (connParams != null) { String dbtype = (String) connParams.get("dbtype"); // app-schema feature types need special treatment if ("app-schema".equals(dbtype)) { String mappingUrl = connParams.get("url").toString(); if (gmlParamsCache != null && gmlParamsCache.containsKey(mappingUrl)) { // avoid looking up the GML version again gmlParams = gmlParamsCache.get(mappingUrl); } else { // use global OWS service to make sure all secondary namespaces // are accessible gmlParams.baseUrl = getBaseUrl("ows", true); // always use WFS 1.1.0 for app-schema layers gmlParams.wfsVersion = org.geotools.wfs.v1_1.WFS.getInstance() .getVersion(); // determine GML version by inspecting the feature type and its super types try { gmlParams.gmlVersion = findGmlVersion(ftInfo); } catch (IOException e) { LOGGER.log(Level.FINE, "Could not determine GML version, using default", e); gmlParams.gmlVersion = null; } // store params in cache if (gmlParamsCache != null) { gmlParamsCache.put(mappingUrl, gmlParams); } } } // TODO: do other data stores have any special needs? else { } } } } } return buildGmlLink(gmlParams); } /** * Returns the GML version used in the feature type's definition. * * <p> * The method recursively climbs up the type hierarchy of the provided feature type, until it finds AbstractFeatureType. Then, the GML version is * determined by looking at the namespace URI. * </p> * * <p> * Please note that this method does not differentiate between GML 2 and GML 3.1.1, but assumes that "http://www.opengis.net/gml" namespace always * refers to GML 3.1.1. * </p> * * @param ftInfo the feature type info * @return the GML version used in the feature type definition * @throws IOException if the underlying datastore instance cannot be retrieved */ String findGmlVersion(FeatureTypeInfo ftInfo) throws IOException { ProgressListener listener = new NullProgressListener(); Name qName = ftInfo.getQualifiedName(); FeatureType fType = ftInfo.getStore().getDataStore(listener).getSchema(qName); return findFeatureTypeGmlVersion(fType); } private String findFeatureTypeGmlVersion(AttributeType featureType) { if (featureType == null) { return null; } if (isAbstractFeatureType(featureType)) { String gmlNamespace = featureType.getName().getNamespaceURI(); if (org.geotools.gml3.GML.NAMESPACE.equals(gmlNamespace)) { // GML 3.1.1 return "gml3"; } else if (org.geotools.gml3.v3_2.GML.NAMESPACE.equals(gmlNamespace)) { // GML 3.2 return GML32OutputFormat.FORMATS.get(0); } else { // should never happen LOGGER.log(Level.FINE, "Cannot determine GML version from AbstractFeatureType type"); return null; } } // recursively check super types AttributeType parent = featureType.getSuper(); return findFeatureTypeGmlVersion(parent); } private boolean isAbstractFeatureType(AttributeType type) { if (type == null) { return false; } Name qName = type.getName(); String localPart = qName.getLocalPart(); String ns = qName.getNamespaceURI(); if ("AbstractFeatureType".equals(localPart) && (org.geotools.gml3.GML.NAMESPACE.equals(ns) || org.geotools.gml3.v3_2.GML.NAMESPACE .equals(ns))) { return true; } else { return false; } } String buildGmlLink(GMLOutputParams gmlParams) { StringBuilder urlBuilder = new StringBuilder(); urlBuilder.append(gmlParams.baseUrl).append("?"); urlBuilder.append("service=WFS").append("&"); urlBuilder.append("version=").append(gmlParams.wfsVersion).append("&"); urlBuilder.append("request=GetFeature").append("&"); urlBuilder.append("typeName=").append(getName()); if (gmlParams.gmlVersion != null) { urlBuilder.append("&"); urlBuilder.append("outputFormat=").append(gmlParams.gmlVersion); } return urlBuilder.toString(); } class GMLOutputParams { String wfsVersion; String gmlVersion; String baseUrl; private GMLOutputParams() { // by default, use WFS 1.0.0 wfsVersion = org.geotools.wfs.v1_0.WFS.getInstance().getVersion(); // by default, infer GML version from WFS version gmlVersion = null; // by default, use virtual ows services baseUrl = getBaseUrl("ows"); } } }