Worldwind public release 0.2
[worldwind-tracker.git] / gov / nasa / worldwind / GeoRSSParser.java
blobf3717c7a8367fb66ce88b88390ac48c6b8b0db85
1 /*
2 Copyright (C) 2001, 2006 United States Government
3 as represented by the Administrator of the
4 National Aeronautics and Space Administration.
5 All Rights Reserved.
6 */
7 package gov.nasa.worldwind;
9 import gov.nasa.worldwind.geom.*;
10 import org.w3c.dom.*;
11 import org.xml.sax.*;
13 import javax.xml.parsers.*;
14 import javax.xml.namespace.*;
15 import java.io.*;
16 import java.util.*;
18 /**
19 * @author tag
20 * @version $Id: GeoRSSParser.java 1686 2007-05-02 21:58:49Z tgaskins $
23 public class GeoRSSParser
25 public static final String GEORSS_URI = "http://www.georss.org/georss";
26 public static final String GML_URI = "http://www.opengis.net/gml";
28 public static List<Renderable> parseFragment(String fragmentString, NamespaceContext nsc)
30 return parseShapes(fixNamespaceQualification(fragmentString));
33 public static List<Renderable> parseShapes(String docString)
35 if (docString == null)
37 String message = WorldWind.retrieveErrMsg("nullValue.StringIsNull");
38 WorldWind.logger().log(java.util.logging.Level.FINE, message);
39 throw new IllegalArgumentException(message);
42 if (docString.length() < 1) // avoid empty strings
43 return null;
45 try
47 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
48 docBuilderFactory.setNamespaceAware(true);
49 DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
50 Document doc = docBuilder.parse(new InputSource(new StringReader(docString)));
52 List<Renderable> shapes = parseShapes(doc);
54 if (shapes == null || shapes.size() < 1)
56 String message = WorldWind.retrieveErrMsg("GeoRSS.NoShapes") + docString;
57 WorldWind.logger().log(java.util.logging.Level.FINE, message);
58 return null;
61 return shapes;
63 catch (ParserConfigurationException e)
65 String message = WorldWind.retrieveErrMsg("GeoRSS.ParserConfigurationException");
66 WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
67 throw new WWRuntimeException(message, e);
69 catch (IOException e)
71 String message = WorldWind.retrieveErrMsg("GeoRSS.IOExceptionParsing") + docString;
72 WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
73 throw new WWRuntimeException(message, e);
75 catch (SAXException e)
77 String message = WorldWind.retrieveErrMsg("GeoRSS.IOExceptionParsing") + docString;
78 WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
79 throw new WWRuntimeException(message, e);
83 private static String fixNamespaceQualification(String xmlString)
85 String lcaseString = xmlString.toLowerCase();
86 StringBuffer qualifiers = new StringBuffer();
88 if (lcaseString.contains("georss:") && !lcaseString.contains(GEORSS_URI))
90 qualifiers.append(" xmlns:georss=\"");
91 qualifiers.append(GEORSS_URI);
92 qualifiers.append("\"");
95 if (lcaseString.contains("gml:") && !lcaseString.contains(GML_URI))
97 qualifiers.append(" xmlns:gml=\"");
98 qualifiers.append(GML_URI);
99 qualifiers.append("\"");
102 if (qualifiers.length() > 0)
104 StringBuffer sb = new StringBuffer();
105 sb.append("<wwdummyelement");
106 sb.append(qualifiers);
107 sb.append(">");
108 sb.append(xmlString);
109 sb.append("</wwdummyelement>");
111 return sb.toString();
114 return xmlString;
117 private static String surroundWithNameSpaces(String docString)
119 StringBuffer sb = new StringBuffer();
120 sb.append("<wwdummyelement");
121 sb.append(" xmlns:georss=\"");
122 sb.append(GEORSS_URI);
123 sb.append("\"");
124 sb.append(" xmlns:gml=\"");
125 sb.append(GML_URI);
126 sb.append("\"");
127 sb.append(">");
128 sb.append(docString);
129 sb.append("</wwdummyelement>");
130 // System.out.println(sb.toString());
132 return sb.toString();
135 public static List<Renderable> parseShapes(File file)
137 if (file == null)
139 String message = WorldWind.retrieveErrMsg("nullValue.FileIsNull");
140 WorldWind.logger().log(java.util.logging.Level.FINE, message);
141 throw new IllegalArgumentException(message);
146 DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
147 docBuilderFactory.setNamespaceAware(false);
148 DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
149 Document doc = docBuilder.parse(file);
151 List<Renderable> shapes = parseShapes(doc);
153 if (shapes == null || shapes.size() < 1)
155 String message = WorldWind.retrieveErrMsg("GeoRSS.NoShapes") + file.getPath();
156 WorldWind.logger().log(java.util.logging.Level.FINE, message);
157 return null;
160 return shapes;
162 catch (ParserConfigurationException e)
164 String message = WorldWind.retrieveErrMsg("GeoRSS.ParserConfigurationException");
165 WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
166 throw new WWRuntimeException(message, e);
168 catch (IOException e)
170 String message = WorldWind.retrieveErrMsg("GeoRSS.IOExceptionParsing") + file.getPath();
171 WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
172 throw new WWRuntimeException(message, e);
174 catch (SAXException e)
176 String message = WorldWind.retrieveErrMsg("GeoRSS.IOExceptionParsing") + file.getPath();
177 WorldWind.logger().log(java.util.logging.Level.FINE, message, e);
178 throw new WWRuntimeException(message, e);
182 public static List<Renderable> parseShapes(Document xmlDoc)
184 if (xmlDoc == null)
186 String message = WorldWind.retrieveErrMsg("nullValue.DocumentIsNull");
187 WorldWind.logger().log(java.util.logging.Level.FINE, message);
188 throw new IllegalArgumentException(message);
191 ArrayList<Node> shapeNodes = new ArrayList<Node>();
192 ArrayList<Node> attributeNodes = new ArrayList<Node>();
194 // Shapes
195 NodeList nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "where");
196 if (nodes != null && nodes.getLength() > 0)
197 addNodes(shapeNodes, nodes);
199 nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "point");
200 if (nodes != null && nodes.getLength() > 0)
201 addNodes(shapeNodes, nodes);
203 nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "line");
204 if (nodes != null && nodes.getLength() > 0)
205 addNodes(shapeNodes, nodes);
207 nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "polygon");
208 if (nodes != null && nodes.getLength() > 0)
209 addNodes(shapeNodes, nodes);
211 nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "box");
212 if (nodes != null && nodes.getLength() > 0)
213 addNodes(shapeNodes, nodes);
215 // Attributes
216 nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "radius");
217 if (nodes != null && nodes.getLength() > 0)
218 addNodes(attributeNodes, nodes);
220 nodes = xmlDoc.getElementsByTagNameNS(GEORSS_URI, "elev");
221 if (nodes != null && nodes.getLength() > 0)
222 addNodes(attributeNodes, nodes);
224 ArrayList<Renderable> shapes = new ArrayList<Renderable>();
226 if (shapeNodes.size() < 1)
227 return null; // No warning here. Let the calling method inform of this case.
229 for (Node node : shapeNodes)
231 Renderable shape = null;
232 String localName = node.getLocalName();
234 if (localName.equals("point"))
235 shape = makePointShape(node, attributeNodes);
237 else if (localName.equals("where"))
238 shape = makeWhereShape(node);
240 else if (localName.equals("line"))
241 shape = makeLineShape(node, attributeNodes);
243 else if (localName.equals("polygon"))
244 shape = makePolygonShape(node, attributeNodes);
246 else if (localName.equals("box"))
247 shape = makeBoxShape(node, attributeNodes);
249 if (shape != null)
250 shapes.add(shape);
253 return shapes;
256 private static void addNodes(ArrayList<Node> nodeList, NodeList nodes)
258 for (int i = 0; i < nodes.getLength(); i++)
260 nodeList.add(nodes.item(i));
264 private static Renderable makeWhereShape(Node node)
266 Node typeNode = findChildByLocalName(node, "Polygon");
267 if (typeNode != null)
268 return makeGMLPolygonShape(typeNode);
270 typeNode = findChildByLocalName(node, "Envelope");
271 if (typeNode != null)
272 return makeGMLEnvelopeShape(typeNode);
274 typeNode = findChildByLocalName(node, "LineString");
275 if (typeNode != null)
276 return makeGMLineStringShape(typeNode);
278 typeNode = findChildByLocalName(node, "Point");
279 if (typeNode != null)
280 return makeGMLPointShape(typeNode);
282 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElementContent") + " where";
283 WorldWind.logger().log(java.util.logging.Level.FINE, message);
284 return null;
287 private static Renderable makeGMLPolygonShape(Node node)
289 Node n = findChildByLocalName(node, "exterior");
290 if (n == null)
292 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElement") + " exterior";
293 WorldWind.logger().log(java.util.logging.Level.FINE, message);
294 return null;
297 n = findChildByLocalName(n, "LinearRing");
298 if (n == null)
300 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElement") + " LinearRing";
301 WorldWind.logger().log(java.util.logging.Level.FINE, message);
302 return null;
305 return makePolygonShape(n, null);
308 private static Renderable makePolygonShape(Node node, Iterable<Node> attrs)
310 String valuesString = node.getTextContent();
311 if (valuesString == null)
313 String message = WorldWind.retrieveErrMsg("GeoRSS.NoCoordinates" + node.getLocalName());
314 WorldWind.logger().log(java.util.logging.Level.FINE, message);
315 return null;
318 ArrayList<Double> values = getDoubleValues(valuesString);
319 if (values.size() < 8 || values.size() % 2 != 0)
321 String message = WorldWind.retrieveErrMsg("GeoRSS.InvalidCoordinateCount" + node.getLocalName());
322 WorldWind.logger().log(java.util.logging.Level.FINE, message);
323 return null;
326 ArrayList<LatLon> positions = new ArrayList<LatLon>();
327 for (int i = 0; i < values.size(); i += 2)
329 positions.add(LatLon.fromDegrees(values.get(i), values.get(i + 1)));
332 double elevation = attrs != null ? getElevation(node, attrs) : 0d;
333 if (elevation != 0)
335 Polyline pl = new Polyline(positions, elevation);
336 pl.setFilled(true);
337 pl.setFollowGreatCircles(true);
339 return pl;
341 else
343 return new SurfacePolygon(positions);
347 private static Renderable makeGMLEnvelopeShape(Node node)
349 Node n = findChildByLocalName(node, "lowerCorner");
350 if (n == null)
352 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElement") + " lowerCorner";
353 WorldWind.logger().log(java.util.logging.Level.FINE, message);
354 return null;
357 String lowerCornerString = n.getTextContent();
358 if (lowerCornerString == null)
360 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElementContent") + " lowerCorner";
361 WorldWind.logger().log(java.util.logging.Level.FINE, message);
362 return null;
365 n = findChildByLocalName(node, "upperCorner");
366 if (n == null)
368 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElement") + " upperCorner";
369 WorldWind.logger().log(java.util.logging.Level.FINE, message);
370 return null;
373 String upperCornerString = n.getTextContent();
374 if (upperCornerString == null)
376 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElementContent") + " upperCorner";
377 WorldWind.logger().log(java.util.logging.Level.FINE, message);
378 return null;
381 ArrayList<Double> lv = getDoubleValues(lowerCornerString);
382 if (lv.size() != 2)
384 String message = WorldWind.retrieveErrMsg("GeoRSS.InvalidCoordinateCount" + " lowerCorner");
385 WorldWind.logger().log(java.util.logging.Level.FINE, message);
386 return null;
389 ArrayList<Double> uv = getDoubleValues(upperCornerString);
390 if (uv.size() != 2)
392 String message = WorldWind.retrieveErrMsg("GeoRSS.InvalidCoordinateCount" + " upperCorner");
393 WorldWind.logger().log(java.util.logging.Level.FINE, message);
394 return null;
397 return new SurfaceQuadrilateral(Sector.fromDegrees(lv.get(0), uv.get(0), lv.get(1), uv.get(1)));
400 private static Renderable makeBoxShape(Node node, Iterable<Node> attrs)
402 String valuesString = node.getTextContent();
403 if (valuesString == null)
405 String message = WorldWind.retrieveErrMsg("GeoRSS.NoCoordinates" + node.getLocalName());
406 WorldWind.logger().log(java.util.logging.Level.FINE, message);
407 return null;
410 ArrayList<Double> p = getDoubleValues(valuesString);
411 if (p.size() != 4)
413 String message = WorldWind.retrieveErrMsg("GeoRSS.InvalidCoordinateCount" + node.getLocalName());
414 WorldWind.logger().log(java.util.logging.Level.FINE, message);
415 return null;
418 double elevation = getElevation(node, attrs);
419 if (elevation != 0)
420 return new Quadrilateral(LatLon.fromDegrees(p.get(0), p.get(1)),
421 LatLon.fromDegrees(p.get(2), p.get(3)), elevation);
422 else
423 return new SurfaceQuadrilateral(Sector.fromDegrees(p.get(0), p.get(2), p.get(1), p.get(3)));
426 private static Renderable makeGMLineStringShape(Node node)
428 Node n = findChildByLocalName(node, "posList");
429 if (n == null)
431 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElement") + " posList";
432 WorldWind.logger().log(java.util.logging.Level.FINE, message);
433 return null;
436 return makeLineShape(n, null);
439 private static Renderable makeLineShape(Node node, Iterable<Node> attrs)
441 String valuesString = node.getTextContent();
442 if (valuesString == null)
444 String message = WorldWind.retrieveErrMsg("GeoRSS.NoCoordinates" + node.getLocalName());
445 WorldWind.logger().log(java.util.logging.Level.FINE, message);
446 return null;
449 ArrayList<Double> values = getDoubleValues(valuesString);
450 if (values.size() < 4)
452 String message = WorldWind.retrieveErrMsg("GeoRSS.InvalidCoordinateCount" + node.getLocalName());
453 WorldWind.logger().log(java.util.logging.Level.FINE, message);
454 return null;
457 ArrayList<LatLon> positions = new ArrayList<LatLon>();
458 for (int i = 0; i < values.size(); i += 2)
460 positions.add(LatLon.fromDegrees(values.get(i), values.get(i + 1)));
463 double elevation = attrs != null ? getElevation(node, attrs) : 0d;
464 if (elevation != 0)
466 Polyline pl = new Polyline(positions, elevation);
467 pl.setFollowGreatCircles(true);
468 return pl;
470 else
472 return new SurfacePolyline(positions);
476 @SuppressWarnings({"UnusedDeclaration"})
477 private static Renderable makeGMLPointShape(Node node)
479 return null; // No shape provided for points. Expect app to use icons.
482 @SuppressWarnings({"UnusedDeclaration"})
483 private static Renderable makePointShape(Node node, Iterable<Node> attrs)
485 return null; // No shape provided for points. Expect app to use icons.
488 private static Node findChildByLocalName(Node parent, String localName)
490 NodeList children = parent.getChildNodes();
491 if (children == null || children.getLength() < 1)
492 return null;
494 for (int i = 0; i < children.getLength(); i++)
496 String ln = children.item(i).getLocalName();
497 if (ln != null && ln.equals(localName))
498 return children.item(i);
501 return null;
504 private static Node findSiblingAttribute(String attrName, Iterable<Node> attribs, Node shapeNode)
506 for (Node attrib : attribs)
508 if (!attrib.getLocalName().equals(attrName))
509 continue;
511 if (attrib.getParentNode().equals(shapeNode.getParentNode()))
512 return attrib;
515 return null;
518 private static ArrayList<Double> getDoubleValues(String stringValues)
520 String[] tokens = stringValues.trim().split("[ ,\n]");
521 if (tokens.length < 1)
522 return null;
524 ArrayList<Double> arl = new ArrayList<Double>();
525 for (String s : tokens)
527 if (s == null || s.length() < 1)
528 continue;
530 double d;
533 d = Double.parseDouble(s);
535 catch (NumberFormatException e)
537 String message = WorldWind.retrieveErrMsg("GeoRSS.NumberFormatException" + s);
538 WorldWind.logger().log(java.util.logging.Level.FINE, message);
539 continue;
542 arl.add(d);
545 return arl;
548 private static double getElevation(Node shapeNode, Iterable<Node> attrs)
550 double elevation = 0d;
552 Node elevNode = findSiblingAttribute("elev", attrs, shapeNode);
553 if (elevNode != null)
555 ArrayList<Double> ev = getDoubleValues(elevNode.getTextContent());
556 if (ev != null && ev.size() > 0)
558 elevation = ev.get(0);
560 else
562 String message = WorldWind.retrieveErrMsg("GeoRSS.MissingElementContent") + " elev";
563 WorldWind.logger().log(java.util.logging.Level.FINE, message);
567 return elevation;