/* * The Unified Mapping Platform (JUMP) is an extensible, interactive GUI * for visualizing and manipulating spatial features with geometry and attributes. * * Copyright (C) 2003 Vivid Solutions * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * For more information, contact: * * Vivid Solutions * Suite #1A * 2328 Government Street * Victoria BC V8T 5G5 * Canada * * (250)385-6040 * www.vividsolutions.com */ // Changed by Uwe Dalluege, uwe.dalluege@rzcn.haw-hamburg.de // to differ between LatLonBoundingBox and BoundingBox // 2005-08-09 package com.vividsolutions.wms; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.LinkedList; import org.apache.log4j.Logger; import org.apache.xerces.parsers.DOMParser; import org.w3c.dom.CharacterData; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.vividsolutions.jump.I18N; import com.vividsolutions.wms.util.XMLTools; /** * Pulls WMS objects out of the XML * @author Chris Hodgson chodgson@refractions.net */ public class Parser { private static Logger LOG = Logger.getLogger(Parser.class); /** * Creates a Parser for dealing with WMS XML. */ public Parser() { } /** * Parses the WMT_MS_Capabilities XML from the given InputStream into * a Capabilities object. * @param service the WMService from which this MapDescriptor is derived * @param inStream the inputStream containing the WMT_MS_Capabilities XML to parse * @return the MapDescriptor object created from the specified XML InputStream */ public Capabilities parseCapabilities( WMService service, InputStream inStream ) throws IOException { if ( WMService.WMS_1_1_1.equals( service.getVersion() ) || WMService.WMS_1_1_0.equals( service.getVersion() ) ){ return parseCapabilities_1_1_1(service, inStream); } return parseCapabilities_1_0_0(service, inStream); } /** * Traverses the DOM tree underneath the specified Node and generates * a corresponding WMSLayer object tree. The returned WMSLayer will be set to * have the specified parent. * @param layerNode a DOM Node which is a <layer> XML element * @return a WMSLayer with complete subLayer tree that corresponds * to the DOM Node provided */ public MapLayer wmsLayerFromNode( Node layerNode ) { String name = null; String title = null; LinkedList<String> srsList = new LinkedList<String>(); LinkedList<MapLayer> subLayers = new LinkedList<MapLayer>(); BoundingBox bbox = null; // I think, bbox is LatLonBoundingBox. // I need a new variable for BoundingBox. // It must be a list because in the OGC document // stands that Layers may have zero or more <BoundingBox> [uwe dalluege] // BoundingBox boundingBox = null; ArrayList<BoundingBox> boundingBoxList = new ArrayList<BoundingBox> ( ); NodeList nl = layerNode.getChildNodes(); for( int i = 0; i< nl.getLength(); i++ ) { Node n = nl.item( i ); try { if( n.getNodeType() == Node.ELEMENT_NODE ) { if( n.getNodeName().equals( "Name" ) ) { name = ((CharacterData)n.getFirstChild()).getData(); } else if( n.getNodeName().equals( "Title" ) ) { title = ((CharacterData)n.getFirstChild()).getData(); } else if( n.getNodeName().equals( "SRS" ) ) { String srsStr = ((CharacterData)n.getFirstChild()).getData(); // split the srs String on spaces while( srsStr.length() > 0 ) { int ws = srsStr.indexOf( ' ' ); if( ws > 0 ) { srsList.add( srsStr.substring( 0, ws ) ); srsStr = srsStr.substring( ws + 1 ); } else { if( srsStr.length() > 0 ) { srsList.add( srsStr ); srsStr = ""; } } } } else if( n.getNodeName().equals( "LatLonBoundingBox" ) ) { bbox = boundingBoxFromNode( n ); boundingBoxList.add ( bbox ); // Check for BoundingBox [uwe dalluege] } else if( n.getNodeName( ).equals( "BoundingBox" ) ) { bbox = boundingBoxFromNode( n ); boundingBoxList.add ( bbox ); } else if( n.getNodeName().equals( "Layer" ) ) { subLayers.add( wmsLayerFromNode( n ) ); } } } catch( Exception e ) { e.printStackTrace(); LOG.error( "Exception caught in wmsLayerFromNode(): " + e.toString() ); } } // call the new constructor with boundingBoxList in MapLayer [uwe dalluege] return new MapLayer ( name, title, srsList, subLayers, bbox, boundingBoxList ); } /** * Creates a new BoundingBox object based on the DOM Node given. * @param n the DOM Node to create the Bounding box from, must be either a * LatLonBoundingBox element or a BoundingBox element * @return a new BoundingBox object based on the DOM Node provided */ public BoundingBox boundingBoxFromNode( Node n ) throws Exception { try { String srs = ""; NamedNodeMap nm = n.getAttributes(); if( n.getNodeName().equals( "LatLonBoundingBox" ) ) { srs = "LatLon"; } else if( n.getNodeName().equals( "BoundingBox" ) ) { srs = nm.getNamedItem( "SRS" ).getNodeValue(); } else { // don't bother... // throw new Exception( I18N.get("com.vividsolutions.wms.Parser.not-a-latlon-boundingbox-element") ); } // could not parse when values equal "inf" // double minx = Double.parseDouble(nm.getNamedItem("minx").getNodeValue()); // double miny = Double.parseDouble(nm.getNamedItem("miny").getNodeValue()); // double maxx = Double.parseDouble(nm.getNamedItem("maxx").getNodeValue()); // double maxy = Double.parseDouble(nm.getNamedItem("maxy").getNodeValue()); // change "inf" values with +/-"Infinity" double minx; if (nm.getNamedItem("minx").getNodeValue().equals("inf")){ minx = Double.NEGATIVE_INFINITY; } else { minx = Double.parseDouble(nm.getNamedItem("minx").getNodeValue()); } double miny; if (nm.getNamedItem("miny").getNodeValue().equals("inf")){ miny = Double.NEGATIVE_INFINITY; } else { miny = Double.parseDouble(nm.getNamedItem("miny").getNodeValue()); } double maxx; if (nm.getNamedItem("maxx").getNodeValue().equals("inf")) { maxx = Double.POSITIVE_INFINITY; } else { maxx = Double.parseDouble(nm.getNamedItem("maxx").getNodeValue()); } double maxy; if (nm.getNamedItem("maxy").getNodeValue().equals("inf")) { maxy = Double.POSITIVE_INFINITY; } else { maxy = Double.parseDouble(nm.getNamedItem("maxy").getNodeValue()); } return new BoundingBox( srs, minx, miny, maxx, maxy ); } catch( Exception e ) { // possible NullPointerException from getNamedItem returning a null // also possible NumberFormatException e.printStackTrace(); throw new Exception( I18N.get("com.vividsolutions.wms.Parser.invalid-bounding-box-element-node")+": " + e.toString() ); } } private Capabilities parseCapabilities_1_0_0( WMService service, InputStream inStream ) throws IOException { MapLayer topLayer = null; String title = null; LinkedList<String> formatList = new LinkedList<String>(); Document doc; try { DOMParser parser = new DOMParser(); parser.setFeature( "http://xml.org/sax/features/validation", false ); parser.parse( new InputSource( inStream ) ); doc = parser.getDocument(); // DEBUG: XMLTools.printNode( doc, "" ); } catch( SAXException saxe ) { throw new IOException( saxe.toString() ); } // get the title try { title = ((CharacterData)XMLTools.simpleXPath( doc, "WMT_MS_Capabilities/Service/Title" ).getFirstChild()).getData(); } catch (Exception e) { // possible NullPointerException if there is no firstChild() // also possible miscast causing an Exception // [uwe dalluege] throw new IOException( "Maybe wrong Capabilities Version! " ); } // get the supported file formats Node formatNode = XMLTools.simpleXPath( doc, "WMT_MS_Capabilities/Capability/Request/Map/Format" ); NodeList nl = formatNode.getChildNodes(); for( int i=0; i < nl.getLength(); i++ ) { Node n = nl.item( i ); if( n.getNodeType() == Node.ELEMENT_NODE ) { formatList.add( n.getNodeName() ); } } // get the top layer topLayer = wmsLayerFromNode( XMLTools.simpleXPath( doc, "WMT_MS_Capabilities/Capability/Layer" ) ); return new Capabilities( service, title, topLayer, formatList ); } //UT TODO move this into a common method ( // private Capabilities parseCapabilities( WMService service, InputStream inStream, // String version) private Capabilities parseCapabilities_1_1_1( WMService service, InputStream inStream ) throws IOException { MapLayer topLayer = null; String title = null; String getMapURL, getFeatureInfoURL; LinkedList<String> formatList = new LinkedList<String>(); Document doc; try { DOMParser parser = new DOMParser(); parser.setFeature( "http://xml.org/sax/features/validation", false ); parser.setFeature( "http://apache.org/xml/features/nonvalidating/load-external-dtd", false ); //was throwing java.io.UTFDataFormatException: Invalid byte 2 of 3-byte UTF-8 sequence. // parser.parse( new InputSource( inStream ) ); InputStreamReader ireader = new InputStreamReader( inStream ); parser.parse( new InputSource( ireader ) ); doc = parser.getDocument(); } catch( SAXException saxe ) { throw new IOException( saxe.toString() ); } // get the title try { title = ((CharacterData)XMLTools.simpleXPath( doc, "WMT_MS_Capabilities/Service/Title" ).getFirstChild()).getData(); } catch (Exception e) { // possible NullPointerException if there is no firstChild() // also possible miscast causing an Exception e.printStackTrace(); } // get the supported file formats // UT was "WMT_MS_Capabilities/Capability/Request/Map/Format" final Node formatNode = XMLTools.simpleXPath( doc, "WMT_MS_Capabilities/Capability/Request/GetMap" ); NodeList nl = formatNode.getChildNodes(); for( int i=0; i < nl.getLength(); i++ ) { Node n = nl.item( i ); if( n.getNodeType() == Node.ELEMENT_NODE && "Format".equals( n.getNodeName() )) { formatList.add( n.getFirstChild().getNodeValue() ); } } // get the possible URLs String xp = "DCPType/HTTP/Get/OnlineResource"; String xlink = "http://www.w3.org/1999/xlink"; Element e = (Element) XMLTools.simpleXPath(formatNode, xp); getMapURL = e.getAttributeNS(xlink, "href"); xp = "WMT_MS_Capabilities/Capability/Request/GetFeatureInfo/DCPType/HTTP/Get/OnlineResource"; e = (Element) XMLTools.simpleXPath(doc, xp); if (e != null) { getFeatureInfoURL = e.getAttributeNS(xlink, "href"); } else { getFeatureInfoURL = ""; } // get the top layer topLayer = wmsLayerFromNode( XMLTools.simpleXPath( doc, "WMT_MS_Capabilities/Capability/Layer" ) ); return new Capabilities( service, title, topLayer, formatList, getMapURL, getFeatureInfoURL ); } }