/* Copyright (C) 2001, 2006 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.formats.georss; import gov.nasa.worldwind.exception.WWRuntimeException; import gov.nasa.worldwind.geom.*; import gov.nasa.worldwind.render.*; import gov.nasa.worldwind.util.Logging; import org.w3c.dom.*; import org.xml.sax.*; import javax.xml.namespace.NamespaceContext; import javax.xml.parsers.*; import java.io.*; import java.util.*; import java.util.logging.Level; /** * @author tag * @version $Id: GeoRSSParser.java 3272 2007-10-10 21:00:19Z tgaskins $ */ public class GeoRSSParser { public static final String GEORSS_URI = "http://www.georss.org/georss"; public static final String GML_URI = "http://www.opengis.net/gml"; public static List<Renderable> parseFragment(String fragmentString, NamespaceContext nsc) { return parseShapes(fixNamespaceQualification(fragmentString)); } public static List<Renderable> parseShapes(String docString) { if (docString == null) { String message = Logging.getMessage("nullValue.StringIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } if (docString.length() < 1) // avoid empty strings return null; try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); docBuilderFactory.setNamespaceAware(true); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.parse(new InputSource(new StringReader(docString))); List<Renderable> shapes = parseShapes(doc); if (shapes == null || shapes.size() < 1) { Logging.logger().log(Level.WARNING, "GeoRSS.NoShapes", docString); return null; } return shapes; } catch (ParserConfigurationException e) { String message = Logging.getMessage("GeoRSS.ParserConfigurationException"); Logging.logger().log(Level.SEVERE, message, e); throw new WWRuntimeException(message, e); } catch (IOException e) { String message = Logging.getMessage("GeoRSS.IOExceptionParsing", docString); Logging.logger().log(Level.SEVERE, message, e); throw new WWRuntimeException(message, e); } catch (SAXException e) { String message = Logging.getMessage("GeoRSS.IOExceptionParsing", docString); Logging.logger().log(Level.SEVERE, message, e); throw new WWRuntimeException(message, e); } } private static String fixNamespaceQualification(String xmlString) { String lcaseString = xmlString.toLowerCase(); StringBuffer qualifiers = new StringBuffer(); if (lcaseString.contains("georss:") && !lcaseString.contains(GEORSS_URI)) { qualifiers.append(" xmlns:georss=\""); qualifiers.append(GEORSS_URI); qualifiers.append("\""); } if (lcaseString.contains("gml:") && !lcaseString.contains(GML_URI)) { qualifiers.append(" xmlns:gml=\""); qualifiers.append(GML_URI); qualifiers.append("\""); } if (qualifiers.length() > 0) { StringBuffer sb = new StringBuffer(); sb.append("<wwdummyelement"); sb.append(qualifiers); sb.append(">"); sb.append(xmlString); sb.append("</wwdummyelement>"); return sb.toString(); } return xmlString; } private static String surroundWithNameSpaces(String docString) { StringBuffer sb = new StringBuffer(); sb.append("<wwdummyelement"); sb.append(" xmlns:georss=\""); sb.append(GEORSS_URI); sb.append("\""); sb.append(" xmlns:gml=\""); sb.append(GML_URI); sb.append("\""); sb.append(">"); sb.append(docString); sb.append("</wwdummyelement>"); // System.out.println(sb.toString()); return sb.toString(); } public static List<Renderable> parseShapes(File file) { if (file == null) { String message = Logging.getMessage("nullValue.FileIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } try { DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); docBuilderFactory.setNamespaceAware(false); DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); Document doc = docBuilder.parse(file); List<Renderable> shapes = parseShapes(doc); if (shapes == null || shapes.size() < 1) { Logging.logger().log(Level.WARNING, "GeoRSS.NoShapes", file.getPath()); return null; } return shapes; } catch (ParserConfigurationException e) { String message = Logging.getMessage("GeoRSS.ParserConfigurationException"); Logging.logger().log(Level.SEVERE, message, e); throw new WWRuntimeException(message, e); } catch (IOException e) { String message = Logging.getMessage("GeoRSS.IOExceptionParsing", file.getPath()); Logging.logger().log(Level.SEVERE, message, e); throw new WWRuntimeException(message, e); } catch (SAXException e) { String message = Logging.getMessage("GeoRSS.IOExceptionParsing", file.getPath()); Logging.logger().log(Level.SEVERE, message, e); throw new WWRuntimeException(message, e); } } public static List<Renderable> parseShapes(Document xmlDoc) { if (xmlDoc == null) { String message = Logging.getMessage("nullValue.DocumentIsNull"); Logging.logger().severe(message); throw new IllegalArgumentException(message); } ArrayList<Node> shapeNodes = new ArrayList<Node>(); ArrayList<Node> attributeNodes = new ArrayList<Node>(); // Shapes NodeList nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "where"); if (nodes != null && nodes.getLength() > 0) addNodes(shapeNodes, nodes); nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "point"); if (nodes != null && nodes.getLength() > 0) addNodes(shapeNodes, nodes); nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "line"); if (nodes != null && nodes.getLength() > 0) addNodes(shapeNodes, nodes); nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "polygon"); if (nodes != null && nodes.getLength() > 0) addNodes(shapeNodes, nodes); nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "box"); if (nodes != null && nodes.getLength() > 0) addNodes(shapeNodes, nodes); // Attributes nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "radius"); if (nodes != null && nodes.getLength() > 0) addNodes(attributeNodes, nodes); nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "elev"); if (nodes != null && nodes.getLength() > 0) addNodes(attributeNodes, nodes); ArrayList<Renderable> shapes = new ArrayList<Renderable>(); if (shapeNodes.size() < 1) return null; // No warning here. Let the calling method inform of this case. for (Node node : shapeNodes) { Renderable shape = null; String localName = node.getLocalName(); if (localName.equals("point")) shape = makePointShape(node, attributeNodes); else if (localName.equals("where")) shape = makeWhereShape(node); else if (localName.equals("line")) shape = makeLineShape(node, attributeNodes); else if (localName.equals("polygon")) shape = makePolygonShape(node, attributeNodes); else if (localName.equals("box")) shape = makeBoxShape(node, attributeNodes); if (shape != null) shapes.add(shape); } return shapes; } private static void addNodes(ArrayList<Node> nodeList, NodeList nodes) { for (int i = 0; i < nodes.getLength(); i++) { nodeList.add(nodes.item(i)); } } private static Renderable makeWhereShape(Node node) { Node typeNode = findChildByLocalName(node, "Polygon"); if (typeNode != null) return makeGMLPolygonShape(typeNode); typeNode = findChildByLocalName(node, "Envelope"); if (typeNode != null) return makeGMLEnvelopeShape(typeNode); typeNode = findChildByLocalName(node, "LineString"); if (typeNode != null) return makeGMLineStringShape(typeNode); typeNode = findChildByLocalName(node, "Point"); if (typeNode != null) return makeGMLPointShape(typeNode); Logging.logger().log(Level.WARNING, "GeoRSS.MissingElementContent", "where"); return null; } private static Renderable makeGMLPolygonShape(Node node) { Node n = findChildByLocalName(node, "exterior"); if (n == null) { Logging.logger().log(Level.WARNING, "GeoRSS.MissingElement", "exterior"); return null; } n = findChildByLocalName(n, "LinearRing"); if (n == null) { Logging.logger().log(Level.WARNING, "GeoRSS.MissingElement", "LinearRing"); return null; } return makePolygonShape(n, null); } private static Renderable makePolygonShape(Node node, Iterable<Node> attrs) { String valuesString = node.getTextContent(); if (valuesString == null) { Logging.logger().log(Level.WARNING, "GeoRSS.NoCoordinates", node.getLocalName()); return null; } ArrayList<Double> values = getDoubleValues(valuesString); if (values.size() < 8 || values.size() % 2 != 0) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", node.getLocalName()); return null; } ArrayList<LatLon> positions = new ArrayList<LatLon>(); for (int i = 0; i < values.size(); i += 2) { positions.add(LatLon.fromDegrees(values.get(i), values.get(i + 1))); } double elevation = attrs != null ? getElevation(node, attrs) : 0d; if (elevation != 0) { Polyline pl = new Polyline(positions, elevation); pl.setFilled(true); pl.setPathType(Polyline.GREAT_CIRCLE); return pl; } else { return new SurfacePolygon(positions); } } private static Renderable makeGMLEnvelopeShape(Node node) { Node n = findChildByLocalName(node, "lowerCorner"); if (n == null) { Logging.logger().log(Level.WARNING, "GeoRSS.MissingElement", " lowerCorner"); return null; } String lowerCornerString = n.getTextContent(); if (lowerCornerString == null) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", " lowerCorner"); return null; } n = findChildByLocalName(node, "upperCorner"); if (n == null) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", " upperCorner"); return null; } String upperCornerString = n.getTextContent(); if (upperCornerString == null) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", " upperCorner"); return null; } ArrayList<Double> lv = getDoubleValues(lowerCornerString); if (lv.size() != 2) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", " lowerCorner"); return null; } ArrayList<Double> uv = getDoubleValues(upperCornerString); if (uv.size() != 2) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", " upperCorner"); return null; } return new SurfaceSector(Sector.fromDegrees(lv.get(0), uv.get(0), lv.get(1), uv.get(1))); } private static Renderable makeBoxShape(Node node, Iterable<Node> attrs) { String valuesString = node.getTextContent(); if (valuesString == null) { Logging.logger().log(Level.WARNING, "GeoRSS.NoCoordinates", node.getLocalName()); return null; } ArrayList<Double> p = getDoubleValues(valuesString); if (p.size() != 4) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", node.getLocalName()); return null; } double elevation = getElevation(node, attrs); if (elevation != 0) return new Quadrilateral(LatLon.fromDegrees(p.get(0), p.get(1)), LatLon.fromDegrees(p.get(2), p.get(3)), elevation); else return new SurfaceSector(Sector.fromDegrees(p.get(0), p.get(2), p.get(1), p.get(3))); } private static Renderable makeGMLineStringShape(Node node) { Node n = findChildByLocalName(node, "posList"); if (n == null) { Logging.logger().log(Level.WARNING, "GeoRSS.MissingElement", "posList"); return null; } return makeLineShape(n, null); } private static Renderable makeLineShape(Node node, Iterable<Node> attrs) { String valuesString = node.getTextContent(); if (valuesString == null) { Logging.logger().log(Level.WARNING, "GeoRSS.NoCoordinates", node.getLocalName()); return null; } ArrayList<Double> values = getDoubleValues(valuesString); if (values.size() < 4) { Logging.logger().log(Level.WARNING, "GeoRSS.InvalidCoordinateCount", node.getLocalName()); return null; } ArrayList<LatLon> positions = new ArrayList<LatLon>(); for (int i = 0; i < values.size(); i += 2) { positions.add(LatLon.fromDegrees(values.get(i), values.get(i + 1))); } double elevation = attrs != null ? getElevation(node, attrs) : 0d; if (elevation != 0) { Polyline pl = new Polyline(positions, elevation); pl.setPathType(Polyline.GREAT_CIRCLE); return pl; } else { return new Polyline(positions, 0); } } @SuppressWarnings({"UnusedDeclaration"}) private static Renderable makeGMLPointShape(Node node) { return null; // No shape provided for points. Expect app to use icons. } @SuppressWarnings({"UnusedDeclaration"}) private static Renderable makePointShape(Node node, Iterable<Node> attrs) { return null; // No shape provided for points. Expect app to use icons. } private static Node findChildByLocalName(Node parent, String localName) { NodeList children = parent.getChildNodes(); if (children == null || children.getLength() < 1) return null; for (int i = 0; i < children.getLength(); i++) { String ln = children.item(i).getLocalName(); if (ln != null && ln.equals(localName)) return children.item(i); } return null; } private static Node findSiblingAttribute(String attrName, Iterable<Node> attribs, Node shapeNode) { for (Node attrib : attribs) { if (!attrib.getLocalName().equals(attrName)) continue; if (attrib.getParentNode().equals(shapeNode.getParentNode())) return attrib; } return null; } private static ArrayList<Double> getDoubleValues(String stringValues) { String[] tokens = stringValues.trim().split("[ ,\n]"); if (tokens.length < 1) return null; ArrayList<Double> arl = new ArrayList<Double>(); for (String s : tokens) { if (s == null || s.length() < 1) continue; double d; try { d = Double.parseDouble(s); } catch (NumberFormatException e) { Logging.logger().log(Level.SEVERE, "GeoRSS.NumberFormatException", s); continue; } arl.add(d); } return arl; } private static double getElevation(Node shapeNode, Iterable<Node> attrs) { double elevation = 0d; Node elevNode = findSiblingAttribute("elev", attrs, shapeNode); if (elevNode != null) { ArrayList<Double> ev = getDoubleValues(elevNode.getTextContent()); if (ev != null && ev.size() > 0) { elevation = ev.get(0); } else { Logging.logger().log(Level.WARNING, "GeoRSS.MissingElementContent", "elev"); } } return elevation; } }