package jeql.command.io.kml; import java.io.IOException; import java.io.StreamTokenizer; import java.io.StringReader; import java.util.ArrayList; import java.util.List; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import jeql.command.io.xml.XMLParseUtil; import jeql.std.geom.GeomFunction; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateList; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; public class PlacemarkParser { private XMLStreamReader xmlRdr; public PlacemarkParser(XMLStreamReader xmlRdr) { this.xmlRdr = xmlRdr; } public Placemark parse(DocumentModel model) throws XMLStreamException { Placemark pm = new Placemark(); XMLParseUtil.consumeStart(xmlRdr, KMLConstants.PLACEMARK); while (xmlRdr.hasNext()) { if (XMLParseUtil.isEndElement(xmlRdr, KMLConstants.PLACEMARK)) { //System.out.println(pm); xmlRdr.next(); break; } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.NAME)) { pm.setName(XMLParseUtil.parseValue(xmlRdr)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.DESCRIPTION)) { pm.setDescription(XMLParseUtil.parseValue(xmlRdr)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.STYLEURL)) { pm.setStyleUrl(XMLParseUtil.parseValue(xmlRdr)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.EXTENDED_DATA)) { parseExtendedData(pm); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.POINT)) { pm.setGeometry(parsePoint(pm)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.LINESTRING)) { pm.setGeometry(parseLineString(pm)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.LINEARRING)) { pm.setGeometry(parseLinearRing(pm)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.POLYGON)) { pm.setGeometry(parsePolygon(pm)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.MULTIGEOMETRY)) { pm.setGeometry(parseMultiGeometry(pm)); } else { // skip any unrecognized elements xmlRdr.next(); } } return pm; } private void parseExtendedData(Placemark pm) throws XMLStreamException { XMLParseUtil.consumeStart(xmlRdr, KMLConstants.EXTENDED_DATA); while (xmlRdr.hasNext()) { if (XMLParseUtil.isEndElement(xmlRdr, KMLConstants.EXTENDED_DATA)) { //System.out.println(pm); xmlRdr.next(); break; } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.DATA)) { pm.setData(XMLParseUtil.readElement(xmlRdr)); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.SCHEMA_DATA)) { pm.setSchemaData(XMLParseUtil.readElement(xmlRdr)); } else { // skip any unrecognized elements xmlRdr.next(); } } } private Polygon parsePolygon(Placemark pm) throws XMLStreamException { XMLParseUtil.consumeStart(xmlRdr, KMLConstants.POLYGON); LinearRing shell = null; List holes = new ArrayList(); while (xmlRdr.hasNext()) { if (XMLParseUtil.isEndElement(xmlRdr, KMLConstants.POLYGON)) { xmlRdr.next(); break; } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.OUTERBOUNDARYIS)) { xmlRdr.next(); shell = parseLinearRing(pm); XMLParseUtil.consumeEnd(xmlRdr, KMLConstants.OUTERBOUNDARYIS); } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.INNERBOUNDARYIS)) { xmlRdr.next(); Geometry hole = parseLinearRing(pm); holes.add(hole); XMLParseUtil.consumeEnd(xmlRdr, KMLConstants.INNERBOUNDARYIS); } else { xmlRdr.next(); } } return GeomFunction.geomFactory.createPolygon(shell, GeomFunction.geomFactory.toLinearRingArray(holes)); } private LineString parseLineString(Placemark pm) throws XMLStreamException { XMLParseUtil.consumeStart(xmlRdr, KMLConstants.LINESTRING); Coordinate[] pts = null; while (xmlRdr.hasNext()) { if (XMLParseUtil.isEndElement(xmlRdr, KMLConstants.LINESTRING)) { xmlRdr.next(); break; } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.COORDINATES)) { pts = parseCoordinates(XMLParseUtil.parseValue(xmlRdr), true); } else { xmlRdr.next(); } } return GeomFunction.geomFactory.createLineString(pts); } private LinearRing parseLinearRing(Placemark pm) throws XMLStreamException { XMLParseUtil.consumeStart(xmlRdr, KMLConstants.LINEARRING); Coordinate[] pts = null; while (xmlRdr.hasNext()) { if (XMLParseUtil.isEndElement(xmlRdr, KMLConstants.LINEARRING)) { xmlRdr.next(); break; } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.COORDINATES)) { pts = parseCoordinates(XMLParseUtil.parseValue(xmlRdr), true); } else { xmlRdr.next(); } } return GeomFunction.geomFactory.createLinearRing(pts); } private Point parsePoint(Placemark pm) throws XMLStreamException { XMLParseUtil.consumeStart(xmlRdr, KMLConstants.POINT); Coordinate[] pts = null; while (xmlRdr.hasNext()) { if (XMLParseUtil.isEndElement(xmlRdr, KMLConstants.POINT)) { xmlRdr.next(); break; } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.COORDINATES)) { pts = parseCoordinates(XMLParseUtil.parseValue(xmlRdr), true); } else { xmlRdr.next(); } } return GeomFunction.geomFactory.createPoint(pts[0]); } private static String[] geometryTag = new String[] { KMLConstants.MULTIGEOMETRY, KMLConstants.POLYGON, KMLConstants.LINEARRING, KMLConstants.LINESTRING, KMLConstants.POINT }; private GeometryCollection parseMultiGeometry(Placemark pm) throws XMLStreamException { XMLParseUtil.consumeStart(xmlRdr, KMLConstants.MULTIGEOMETRY); List geoms = new ArrayList(); while (xmlRdr.hasNext()) { if (XMLParseUtil.isEndElement(xmlRdr, KMLConstants.MULTIGEOMETRY)) { xmlRdr.next(); break; } else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.MULTIGEOMETRY)) geoms.add(parseMultiGeometry(pm)); else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.POLYGON)) geoms.add(parsePolygon(pm)); else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.LINEARRING)) geoms.add(parseLinearRing(pm)); else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.LINESTRING)) geoms.add(parseLineString(pm)); else if (XMLParseUtil.isStartElement(xmlRdr, KMLConstants.POINT)) geoms.add(parsePoint(pm)); else { xmlRdr.next(); } } return GeomFunction.geomFactory.createGeometryCollection( GeomFunction.geomFactory.toGeometryArray(geoms)); } private static Coordinate[] array = new Coordinate[0]; private Coordinate[] parseCoordinates(String coordStr, boolean closeRing) { StreamTokenizer st = new StreamTokenizer(new StringReader(coordStr)); st.parseNumbers(); CoordinateList coordinates = new CoordinateList(); try { coordinates.add(parseCoordinate(st)); while (hasMoreTokens(st)) { coordinates.add(parseCoordinate(st)); } } catch (IOException ex) { // should never happen - throw illegal state exception throw new IllegalStateException( "IOException during coordinate string parsing"); } // close ring if required if (closeRing) coordinates.closeRing(); return coordinates.toCoordinateArray(); /* * System.out.println(coordStr); // TODO: parse it! return new Coordinate[] { * new Coordinate(),new Coordinate(),new Coordinate(),new Coordinate() }; */ } private Coordinate parseCoordinate(StreamTokenizer st) throws IOException { Coordinate coord = new Coordinate(); coord.x = parseNumber(st); parseComma(st); coord.y = parseNumber(st); if (isCommaNext(st)) { parseComma(st); coord.z = parseNumber(st); } return coord; } private double parseNumber(StreamTokenizer st) throws IOException { int type = st.nextToken(); if (type == StreamTokenizer.TT_NUMBER) return st.nval; // TODO: throw parse exception here return 0.0; } private void parseComma(StreamTokenizer st) throws IOException { int type = st.nextToken(); if (type == ',') return; // TODO: throw parse exception here } private boolean isCommaNext(StreamTokenizer st) throws IOException { int type = st.nextToken(); st.pushBack(); return type == ','; } private boolean hasMoreTokens(StreamTokenizer st) throws IOException { int type = st.nextToken(); st.pushBack(); return type != StreamTokenizer.TT_EOF; } }