/*
* Copyright (c) 2016 Fraunhofer IGD
*
* All rights reserved. This program and the accompanying materials are made
* available 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.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Fraunhofer IGD <http://www.igd.fraunhofer.de/>
*/
package de.fhg.igd.mapviewer.server.wms.capabilities;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import eu.esdihumboldt.util.http.ProxyUtil;
/**
* WMS capabilities
*
* @author Simon Templer
*/
public class WMSCapabilities {
private static final DocumentBuilderFactory builderFactory = DocumentBuilderFactory
.newInstance();
private static final XPathFactory xpathFactory = XPathFactory.newInstance();
private final String version;
private final String title;
private final String mapURL;
private final Set<String> formats = new LinkedHashSet<String>();
private final Set<String> exceptionFormats = new LinkedHashSet<String>();
private final Set<String> supportedSRS = new LinkedHashSet<String>();
private final Map<String, WMSBounds> boundingBoxes = new HashMap<String, WMSBounds>();
private final List<Layer> layers = new ArrayList<Layer>();
/**
* Constructor
*
* @param capabilitiesURI the URI of the capabilities document
*
* @throws WMSCapabilitiesException if loading the capabilities failed
*/
private WMSCapabilities(URI capabilitiesURI) throws WMSCapabilitiesException {
Document document;
try {
document = getDocument(capabilitiesURI);
} catch (Exception e) {
throw new WMSCapabilitiesException(e.getLocalizedMessage(), e);
}
XPath xpath = xpathFactory.newXPath();
try {
// [version] states the WMS version the corresponding code is
// compatible to (may be incomplete)
// only mandatory tags will be evaluated (exceptions: Layer)
// get main node [1.1.1]
Node main = ((NodeList) xpath.evaluate("WMT_MS_Capabilities", document, //$NON-NLS-1$
XPathConstants.NODESET)).item(0);
// determine version [1.1.1]
version = main.getAttributes().getNamedItem("version").getTextContent(); //$NON-NLS-1$
// determine title [1.1.1]
Node titleNode = ((NodeList) xpath.evaluate("WMT_MS_Capabilities/Service/Title", //$NON-NLS-1$
document, XPathConstants.NODESET)).item(0);
title = titleNode.getTextContent();
// GetMap capability
// GetMap formats [1.1.1]
NodeList mapFormats = (NodeList) xpath.evaluate(
"WMT_MS_Capabilities/Capability/Request/GetMap/Format", //$NON-NLS-1$
document, XPathConstants.NODESET);
for (int i = 0; i < mapFormats.getLength(); i++) {
Node formatNode = mapFormats.item(i);
formats.add(formatNode.getTextContent());
}
// GetMap URL [1.1.1], assumes there is a HTTP/Get URL given in the
// document
NodeList urlNodes = (NodeList) xpath.evaluate(
"WMT_MS_Capabilities/Capability/Request/GetMap/DCPType/HTTP/Get/OnlineResource", //$NON-NLS-1$
document, XPathConstants.NODESET);
if (urlNodes.getLength() > 0) {
mapURL = urlNodes.item(0).getAttributes().getNamedItem("xlink:href") //$NON-NLS-1$
.getTextContent();
}
else {
throw new WMSCapabilitiesException(
"No HTTP Get URL defined for the GetMap request"); //$NON-NLS-1$
}
// Exception formats [1.1.1]
NodeList exceptions = (NodeList) xpath.evaluate(
"WMT_MS_Capabilities/Capability/Exception/Format", //$NON-NLS-1$
document, XPathConstants.NODESET);
for (int i = 0; i < exceptions.getLength(); i++) {
Node formatNode = exceptions.item(i);
exceptionFormats.add(formatNode.getTextContent());
}
// Layer
// Layer SRS [1.1.1]
NodeList srsNodes = (NodeList) xpath.evaluate(
"WMT_MS_Capabilities/Capability/Layer/SRS", //$NON-NLS-1$
document, XPathConstants.NODESET);
for (int i = 0; i < srsNodes.getLength(); i++) {
Node srsNode = srsNodes.item(i);
String srsText = srsNode.getTextContent();
Pattern pattern = Pattern.compile("[Ee][Pp][Ss][Gg]:\\d*");
Matcher matcher = pattern.matcher(srsText);
while (matcher.find()) {
String srs = matcher.group();
srs = "EPSG:" + srs.substring(5);
supportedSRS.add(srs);
}
// supportedSRS.add(srsNode.getTextContent());
}
// Layer Bounding Boxes [1.1.1]
NodeList bbNodes = (NodeList) xpath.evaluate(
"WMT_MS_Capabilities/Capability/Layer/BoundingBox", //$NON-NLS-1$
document, XPathConstants.NODESET);
for (int i = 0; i < bbNodes.getLength(); i++) {
Node bbNode = bbNodes.item(i);
String srs = bbNode.getAttributes().getNamedItem("SRS").getTextContent(); //$NON-NLS-1$
WMSBounds box = new WMSBounds(srs,
Double.parseDouble(
bbNode.getAttributes().getNamedItem("minx").getTextContent()), //$NON-NLS-1$
Double.parseDouble(
bbNode.getAttributes().getNamedItem("miny").getTextContent()), //$NON-NLS-1$
Double.parseDouble(
bbNode.getAttributes().getNamedItem("maxx").getTextContent()), //$NON-NLS-1$
Double.parseDouble(
bbNode.getAttributes().getNamedItem("maxy").getTextContent()) //$NON-NLS-1$
);
boundingBoxes.put(srs, box);
}
// WGS84 bounding box
if (!boundingBoxes.containsKey("EPSG:4326")) { //$NON-NLS-1$
NodeList llNodes = (NodeList) xpath.evaluate(
"WMT_MS_Capabilities/Capability/Layer/LatLonBoundingBox", //$NON-NLS-1$
document, XPathConstants.NODESET);
if (llNodes.getLength() > 0) {
Node bbNode = llNodes.item(0);
String srs = "EPSG:4326"; //$NON-NLS-1$
WMSBounds box = new WMSBounds(srs,
Double.parseDouble(
bbNode.getAttributes().getNamedItem("minx").getTextContent()), //$NON-NLS-1$
Double.parseDouble(
bbNode.getAttributes().getNamedItem("miny").getTextContent()), //$NON-NLS-1$
Double.parseDouble(
bbNode.getAttributes().getNamedItem("maxx").getTextContent()), //$NON-NLS-1$
Double.parseDouble(
bbNode.getAttributes().getNamedItem("maxy").getTextContent()) //$NON-NLS-1$
);
boundingBoxes.put(srs, box);
}
}
// Layers [1.1.1]
NodeList layerNodes = (NodeList) xpath.evaluate(
"WMT_MS_Capabilities/Capability/Layer/Layer", //$NON-NLS-1$
document, XPathConstants.NODESET);
for (int i = 0; i < layerNodes.getLength(); i++) {
Node layerNode = layerNodes.item(i);
NodeList children = layerNode.getChildNodes();
String name = null;
String title = null;
String description = null;
for (int j = 0; j < children.getLength(); j++) {
Node child = children.item(j);
String nodeName = child.getNodeName();
if (nodeName.equals("Name")) { //$NON-NLS-1$
name = child.getTextContent();
}
else if (nodeName.equals("Title")) { //$NON-NLS-1$
title = child.getTextContent();
}
else if (nodeName.equals("Abstract")) { //$NON-NLS-1$
description = child.getTextContent();
}
}
if (name != null) {
Layer layer = new Layer(name, title, description);
layers.add(layer);
}
}
} catch (WMSCapabilitiesException e) {
throw e;
} catch (Exception e) {
throw new WMSCapabilitiesException("Document is no valid WMS Capabilities document", e); //$NON-NLS-1$
}
}
/**
* Load an XML document form an URI
*
* @param uri the URI
* @return the XML document if loading was successful
* @throws ParserConfigurationException if an error occurred configuring the
* document parser
* @throws IOException if an error occurred reading the document
* @throws SAXException if an error occurred parsing the document
* @throws MalformedURLException if creating an URL from the given URI fails
*/
private static Document getDocument(URI uri)
throws ParserConfigurationException, MalformedURLException, SAXException, IOException {
DocumentBuilder builder = builderFactory.newDocumentBuilder();
Proxy proxy = ProxyUtil.findProxy(uri);
return builder.parse(uri.toURL().openConnection(proxy).getInputStream());
}
/**
* Get the capabilities of a WMS service
*
* @param capabilitiesURI the URI of the capabilities document
* @return the WMS capabilities
*
* @throws WMSCapabilitiesException if reading the capabilities failed
*/
public static WMSCapabilities getCapabilities(URI capabilitiesURI)
throws WMSCapabilitiesException {
return new WMSCapabilities(capabilitiesURI);
}
/**
* @return the version
*/
public String getVersion() {
return version;
}
/**
* @return the title
*/
public String getTitle() {
return title;
}
/**
* @return the mapURL
*/
public String getMapURL() {
return mapURL;
}
/**
* @return the formats
*/
public Set<String> getFormats() {
return formats;
}
/**
* @return the exceptionFormats
*/
public Set<String> getExceptionFormats() {
return exceptionFormats;
}
/**
* @return the supportedSRS
*/
public Set<String> getSupportedSRS() {
return supportedSRS;
}
/**
* @return the boundingBoxes
*/
public Map<String, WMSBounds> getBoundingBoxes() {
return boundingBoxes;
}
/**
* @return the layers
*/
public List<Layer> getLayers() {
return layers;
}
}