/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) * * This library 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; * version 2.1 of the License. * * This library 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 * Lesser General Public License for more details. */ package org.geotools.data.wms; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.swing.Icon; import javax.swing.ImageIcon; import org.geotools.data.ResourceInfo; import org.geotools.data.ServiceInfo; import org.geotools.data.ows.AbstractOpenWebService; import org.geotools.data.ows.CRSEnvelope; import org.geotools.data.ows.GetCapabilitiesRequest; import org.geotools.data.ows.GetCapabilitiesResponse; import org.geotools.data.ows.Layer; import org.geotools.data.ows.OperationType; import org.geotools.data.ows.Specification; import org.geotools.data.ows.WMSCapabilities; import org.geotools.data.wms.request.DescribeLayerRequest; import org.geotools.data.wms.request.GetFeatureInfoRequest; import org.geotools.data.wms.request.GetLegendGraphicRequest; import org.geotools.data.wms.request.GetMapRequest; import org.geotools.data.wms.request.GetStylesRequest; import org.geotools.data.wms.request.PutStylesRequest; import org.geotools.data.wms.response.DescribeLayerResponse; import org.geotools.data.wms.response.GetFeatureInfoResponse; import org.geotools.data.wms.response.GetLegendGraphicResponse; import org.geotools.data.wms.response.GetMapResponse; import org.geotools.data.wms.response.GetStylesResponse; import org.geotools.data.wms.response.PutStylesResponse; import org.geotools.data.wms.xml.WMSSchema; import org.geotools.geometry.GeneralEnvelope; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.ows.ServiceException; import org.geotools.referencing.CRS; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.opengis.referencing.FactoryException; import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * WebMapServer is a class representing a WMS. It is used to access the * Capabilities document and perform requests. It supports multiple versions * and will perform version negotiation automatically and use the highest * known version that the server can communicate. * * If restriction of versions to be used is desired, this class should be * subclassed and it's setupSpecifications() method over-ridden. It should * add which version/specifications are to be used to the specs array. See * the current implementation for an example. * * Example usage: * <code><pre> * WebMapServer wms = new WebMapServer("http://some.example.com/wms"); * WMSCapabilities capabilities = wms.getCapabilities(); * GetMapRequest request = wms.getMapRequest(); * * ... //configure request * * GetMapResponse response = (GetMapResponse) wms.issueRequest(request); * * ... //extract image from the response * </pre></code> * * @author Richard Gould, Refractions Research * * @source $URL$ */ public class WebMapServer extends AbstractOpenWebService<WMSCapabilities,Layer> { /** * Class quickly describing Web Map Service. * * @author Jody */ protected class WMSInfo implements ServiceInfo { private Set<String> keywords; private Icon icon; WMSInfo(){ keywords = new HashSet<String>(); if( capabilities.getService() != null ){ String array[] = capabilities.getService().getKeywordList(); if( array != null ){ keywords.addAll( Arrays.asList( array )); } } keywords.add("WMS"); keywords.add( serverURL.toString() ); URL globe2 = WebMapServer.class.getResource("Globe2.png"); icon = new ImageIcon( globe2 ); } public String getDescription() { String description = null; if (capabilities != null && capabilities.getService() != null) { description = capabilities.getService().get_abstract(); } if( description == null && serverURL != null) { description = "Web Map Server "+serverURL; } return description; } public Icon getIcon() { return icon; } public Set<String> getKeywords() { return keywords; } public URI getPublisher() { try { return capabilities.getService().getContactInformation().getContactInfo().getOnLineResource().getLinkage(); } catch( NullPointerException publisherNotAvailable ){ } try { return new URI( serverURL.getProtocol()+":"+serverURL.getHost() ); } catch (URISyntaxException e) { } return null; } /** * We are a Web Map Service: * @return WMSSchema.NAMESPACE; */ public URI getSchema() { return WMSSchema.NAMESPACE; } /** * The source of this WMS is the capabilities document. * <p> * We make an effort here to look in the capabilities document * provided for the unambiguous capabilities URI. This covers * the case where the capabilities document has been cached * on disk and we are restoring a WebMapServer instance. */ public URI getSource() { try { URL source = getCapabilities().getRequest().getGetCapabilities().getGet(); return source.toURI(); } catch( NullPointerException huh ){ } catch (URISyntaxException e) { } try { return serverURL.toURI(); } catch (URISyntaxException e) { return null; } } public String getTitle() { if (capabilities != null && capabilities.getService() != null) { return capabilities.getService().getTitle(); } else if (serverURL == null) { return "Unavailable"; } else { return serverURL.toString(); } } } /** * Quickly describe a layer. * * @author Jody */ public class LayerInfo implements ResourceInfo { private ReferencedEnvelope bounds; private Set<String> keywords; private Icon icon; private Layer layer; LayerInfo( Layer layer ){ this.layer = layer; org.opengis.geometry.Envelope env = null; CoordinateReferenceSystem crs = null; if (layer.getBoundingBoxes().isEmpty()) { env = layer.getEnvelope(DefaultGeographicCRS.WGS84); } else { CRSEnvelope bbox; String epsg4326 = "EPSG:4326"; String epsg4269 = "EPSG:4269"; if (layer.getBoundingBoxes().containsKey(epsg4326)) { bbox = layer.getBoundingBoxes().get(epsg4326); } else if (layer.getBoundingBoxes().containsKey(epsg4269)) { bbox = layer.getBoundingBoxes().get(epsg4269); } else { bbox = layer.getBoundingBoxes().values().iterator().next(); } try { crs = CRS.decode(bbox.getEPSGCode()); env = new ReferencedEnvelope(bbox.getMinX(), bbox.getMaxX(), bbox.getMinY(), bbox.getMaxY(), crs); } catch (NoSuchAuthorityCodeException e) { crs = DefaultGeographicCRS.WGS84; env = layer.getEnvelope(crs); } catch (FactoryException e) { crs = DefaultGeographicCRS.WGS84; env = layer.getEnvelope(crs); } } bounds = new ReferencedEnvelope( env.getMinimum(0), env.getMaximum(0), env.getMinimum(1), env.getMaximum(1), crs); String source = getInfo().getSource().toString(); keywords = new TreeSet<String>(); if (layer.getKeywords() != null) { List<String> more = Arrays.asList( layer.getKeywords() ); keywords.addAll( more ); } if( layer.getName() != null ){ keywords.add( layer.getName() ); } if( layer.getTitle() != null ){ keywords.add( layer.getTitle() ); } keywords.add( capabilities.getService().getName() ); keywords.add( source ); keywords.addAll( getInfo().getKeywords() ); } public ReferencedEnvelope getBounds() { return bounds; } public CoordinateReferenceSystem getCRS() { return bounds.getCoordinateReferenceSystem(); } public String getDescription() { String description = layer.get_abstract(); if ( description != null && description.length() != 0) { return description; } else { return getCapabilities().getService().get_abstract(); } } public synchronized Icon getIcon() { if( icon == null ){ URL image = WebMapServer.class.getResource("image.png"); icon = new ImageIcon( image ); if ( layer.getChildren() != null && layer.getChildren().length != 0) { // Do not request "parent" layer graphics - this kills Mapserver return icon; } OperationType getLegendGraphic = getCapabilities().getRequest().getGetLegendGraphic(); if( getLegendGraphic == null ){ // this wms server does not support legend graphics return icon; } GetLegendGraphicRequest request = createGetLegendGraphicRequest(); request.setLayer( layer.getName() ); request.setWidth("16"); request.setHeight("16"); String desiredFormat = null; List<String> formats = getLegendGraphic.getFormats(); if (formats.contains("image/png")) { desiredFormat = "image/png"; } else if (formats.contains("image/gif")) { desiredFormat = "image/gif"; } else { // there is no format that I am sure we can deal with :-( // the danger here is requesting PDF from geoserver // (because everyone wants a PDF legend graphics - wot?) return icon; } request.setFormat(desiredFormat); request.setStyle(""); URL legendGraphics = request.getFinalURL(); icon = new ImageIcon( legendGraphics ); } return icon; } public Set<String> getKeywords() { return keywords; } public String getName() { return layer.getName(); } public URI getSchema() { return getInfo().getSchema(); } public String getTitle() { String title = layer.getTitle(); if (title != null && title.length() != 0) { return title; } else { // often the "root" layer has no title, the service title // is what is intended return getCapabilities().getService().getTitle(); } } } /** * Creates a new WebMapServer from a WMSCapablitiles document. * <p> * The implementation assumes that the server is located at: * capabilities.getRequest().getGetCapabilities().getGet() * * @param capabilities * @throws IOException * @throws ServiceException */ public WebMapServer( WMSCapabilities capabilities ) throws IOException, ServiceException { super( capabilities, capabilities.getRequest().getGetCapabilities().getGet() ); } /** * Creates a new WebMapServer instance and attempts to retrieve the * Capabilities document specified by serverURL. * * @param serverURL a URL that points to the capabilities document of a server * @throws IOException if there is an error communicating with the server * @throws ServiceException if the server responds with an error */ public WebMapServer( final URL serverURL ) throws IOException, ServiceException { super(serverURL); } /** * Creates a new WebMapServer instance and attempts to retrieve the * Capabilities document specified by serverURL. * * @param serverURL a URL that points to the capabilities document of a server * @param timeout a time to be wait a server response * @throws IOException if there is an error communicating with the server * @throws ServiceException if the server responds with an error */ public WebMapServer( final URL serverURL, int timeout ) throws IOException, ServiceException { super(serverURL,timeout); } /** * Sets up the specifications/versions that this server is capable of * communicating with. */ protected void setupSpecifications() { specs = new Specification[4]; specs[0] = new WMS1_0_0(); specs[1] = new WMS1_1_0(); specs[2] = new WMS1_1_1(); specs[3] = new WMS1_3_0(); } @Override protected ServiceInfo createInfo() { return new WMSInfo(); } protected ResourceInfo createInfo( Layer layer ){ return new LayerInfo( (Layer) layer ); } public GetCapabilitiesResponse issueRequest(GetCapabilitiesRequest request) throws IOException, ServiceException { return (GetCapabilitiesResponse) internalIssueRequest(request); } public GetMapResponse issueRequest(GetMapRequest request) throws IOException, ServiceException { return (GetMapResponse) internalIssueRequest(request); } public GetFeatureInfoResponse issueRequest(GetFeatureInfoRequest request) throws IOException, ServiceException { return (GetFeatureInfoResponse) internalIssueRequest(request); } public DescribeLayerResponse issueRequest(DescribeLayerRequest request) throws IOException, ServiceException { return (DescribeLayerResponse) internalIssueRequest(request); } public GetLegendGraphicResponse issueRequest(GetLegendGraphicRequest request) throws IOException, ServiceException { return (GetLegendGraphicResponse) internalIssueRequest(request); } public GetStylesResponse issueRequest(GetStylesRequest request) throws IOException, ServiceException { return (GetStylesResponse) internalIssueRequest(request); } public PutStylesResponse issueRequest(PutStylesRequest request) throws IOException, ServiceException { return (PutStylesResponse) internalIssueRequest(request); } /** * Get the getCapabilities document. If there was an error parsing it * during creation, it will return null (and it should have thrown an * exception during creation). * * @return a WMSCapabilities object, representing the Capabilities of the server */ public WMSCapabilities getCapabilities() { return (WMSCapabilities) capabilities; } private WMSSpecification getSpecification() { return (WMSSpecification) specification; } private URL findURL(OperationType operation) { if (operation.getGet() != null) { return operation.getGet(); } return serverURL; } /** * Creates a GetMapRequest that can be configured and then passed to * issueRequest(). * * @return a configureable GetMapRequest object */ public GetMapRequest createGetMapRequest() { URL onlineResource = findURL(getCapabilities().getRequest().getGetMap()); return (GetMapRequest) getSpecification().createGetMapRequest(onlineResource); } /** * Creates a GetFeatureInfoRequest that can be configured and then passed to * issueRequest(). * * @param getMapRequest a previous configured GetMapRequest * @return a GetFeatureInfoRequest * @throws UnsupportedOperationException if the server does not support GetFeatureInfo */ public GetFeatureInfoRequest createGetFeatureInfoRequest( GetMapRequest getMapRequest ) { if (getCapabilities().getRequest().getGetFeatureInfo() == null) { throw new UnsupportedOperationException("This Web Map Server does not support GetFeatureInfo requests"); } URL onlineResource = findURL(getCapabilities().getRequest().getGetFeatureInfo()); GetFeatureInfoRequest request = getSpecification().createGetFeatureInfoRequest(onlineResource, getMapRequest); return request; } public DescribeLayerRequest createDescribeLayerRequest() throws UnsupportedOperationException { if (getCapabilities().getRequest().getDescribeLayer() == null ) { throw new UnsupportedOperationException("Server does not specify a DescribeLayer operation. Cannot be performed"); } URL onlineResource = getCapabilities().getRequest().getDescribeLayer().getGet(); if (onlineResource == null) { onlineResource = serverURL; } DescribeLayerRequest request = getSpecification().createDescribeLayerRequest(onlineResource); return request; } public GetLegendGraphicRequest createGetLegendGraphicRequest() throws UnsupportedOperationException { if (getCapabilities().getRequest().getGetLegendGraphic() == null) { throw new UnsupportedOperationException("Server does not specify a GetLegendGraphic operation. Cannot be performed"); } URL onlineResource = getCapabilities().getRequest().getGetLegendGraphic().getGet(); if (onlineResource == null) { onlineResource = serverURL; } GetLegendGraphicRequest request = getSpecification().createGetLegendGraphicRequest(onlineResource); return request; } public GetStylesRequest createGetStylesRequest() throws UnsupportedOperationException{ if (getCapabilities().getRequest().getGetStyles() == null) { throw new UnsupportedOperationException("Server does not specify a GetStyles operation. Cannot be performed"); } URL onlineResource = getCapabilities().getRequest().getGetStyles().getGet(); if (onlineResource == null) { onlineResource = serverURL; } GetStylesRequest request = getSpecification().createGetStylesRequest(onlineResource); return request; } public PutStylesRequest createPutStylesRequest() throws UnsupportedOperationException { if (getCapabilities().getRequest().getPutStyles() == null) { throw new UnsupportedOperationException("Server does not specify a PutStyles operation. Cannot be performed"); } URL onlineResource = getCapabilities().getRequest().getPutStyles().getGet(); if (onlineResource == null) { onlineResource = serverURL; } PutStylesRequest request = getSpecification().createPutStylesRequest(onlineResource); return request; } /** * Given a layer and a coordinate reference system, will locate an envelope * for that layer in that CRS. If the layer is declared to support that CRS, * but no envelope can be found, it will try to calculate an appropriate * bounding box. * * If null is returned, no valid bounding box could be found and one couldn't * be transformed from another. * * @param layer * @param crs * @return an Envelope containing a valid bounding box, or null if none are found */ public GeneralEnvelope getEnvelope(Layer layer, CoordinateReferenceSystem crs) { return layer.getEnvelope(crs); } }