package com.revolsys.record.io.format.kml; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import javax.xml.stream.XMLStreamException; import com.revolsys.collection.iterator.AbstractIterator; import com.revolsys.geometry.io.GeometryReader; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.LinearRing; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.Polygon; import com.revolsys.geometry.model.impl.LineStringDouble; import com.revolsys.geometry.model.impl.PointDouble; import com.revolsys.record.io.format.xml.StaxReader; import com.revolsys.spring.resource.Resource; import com.revolsys.util.Property; public class KmlGeometryReader extends AbstractIterator<Geometry> implements GeometryReader, Kml22Constants { private GeometryFactory geometryFactory = GeometryFactory.floating3(COORDINATE_SYSTEM_ID); private StaxReader in; public KmlGeometryReader(final InputStream in) { this.in = StaxReader.newXmlReader(in); } public KmlGeometryReader(final Resource resource) { this.in = StaxReader.newXmlReader(resource); } @Override protected void closeDo() { if (this.in != null) { this.in.close(); } this.geometryFactory = null; this.in = null; } public GeometryFactory getGeometryFactory() { return this.geometryFactory; } @Override protected Geometry getNext() { try { final int depth = 0; if (this.in.skipToStartElements(depth, MULTI_GEOMETRY, POINT, LINE_STRING, POLYGON)) { final Geometry geometry = parseGeometry(); if (geometry == null) { throw new NoSuchElementException(); } else { return geometry; } } else { throw new NoSuchElementException(); } } catch (final XMLStreamException e) { throw new RuntimeException(e.getMessage(), e); } } @Override protected void initDo() { this.in.skipToStartElement(); } private LineString parseCoordinates() throws XMLStreamException { this.in.requireLocalName(COORDINATES); final String coordinatesListString = this.in.getElementText(); if (Property.hasValue(coordinatesListString)) { int axisCount = 2; final String[] coordinatesListArray = coordinatesListString.trim().split("\\s+"); final List<Point> points = new ArrayList<>(); for (final String coordinatesString : coordinatesListArray) { final String[] coordinatesArray = coordinatesString.split(","); final double[] coordinates = new double[coordinatesArray.length]; for (int axisIndex = 0; axisIndex < coordinatesArray.length; axisIndex++) { final String coordinate = coordinatesArray[axisIndex]; coordinates[axisIndex] = Double.valueOf(coordinate); } axisCount = Math.max(axisCount, coordinates.length); points.add(new PointDouble(coordinates)); } this.in.skipToEndElement(); return new LineStringDouble(axisCount, points); } else { return null; } } private Geometry parseGeometry() throws XMLStreamException { if (this.in.isStartElementLocalName(MULTI_GEOMETRY)) { return parseMultiGeometry(); } else if (this.in.isStartElementLocalName(POINT)) { return parsePoint(); } else if (this.in.isStartElementLocalName(LINE_STRING)) { return parseLineString(); } else if (this.in.isStartElementLocalName(POLYGON)) { return parsePolygon(); } else { return null; } } private LinearRing parseLinearRing() throws XMLStreamException { this.in.requireLocalName(LINEAR_RING); LineString points = null; final int depth = this.in.getDepth(); while (this.in.skipToStartElements(depth, COORDINATES)) { if (points == null && this.in.isStartElementLocalName(COORDINATES)) { points = parseCoordinates(); this.in.skipToEndElement(); } } if (points == null) { return this.geometryFactory.linearRing(); } else { final int axisCount = points.getAxisCount(); final GeometryFactory geometryFactory = this.geometryFactory.convertAxisCount(axisCount); return geometryFactory.linearRing(points); } } private LineString parseLineString() throws XMLStreamException { this.in.requireLocalName(LINE_STRING); LineString points = null; final int depth = this.in.getDepth(); while (this.in.skipToStartElements(depth, COORDINATES)) { if (points == null && this.in.isStartElementLocalName(COORDINATES)) { points = parseCoordinates(); } } if (points == null) { return this.geometryFactory.lineString(); } else { final int axisCount = points.getAxisCount(); final GeometryFactory geometryFactory = this.geometryFactory.convertAxisCount(axisCount); return geometryFactory.lineString(points); } } private Geometry parseMultiGeometry() throws XMLStreamException { int axisCount = 2; final List<Geometry> geometries = new ArrayList<>(); while (this.in.skipToChildStartElements(POINT, LINE_STRING, POLYGON)) { final Geometry geometry = parseGeometry(); if (geometry != null) { axisCount = Math.max(axisCount, geometry.getAxisCount()); geometries.add(geometry); } } final GeometryFactory geometryFactory = this.geometryFactory.convertAxisCount(axisCount); final Geometry geometryCollection = geometryFactory.geometry(geometries); return geometryCollection; } private Point parsePoint() throws XMLStreamException { this.in.requireLocalName(POINT); LineString points = null; final int depth = this.in.getDepth(); while (this.in.skipToStartElements(depth, COORDINATES)) { if (points == null && this.in.isStartElementLocalName(COORDINATES)) { points = parseCoordinates(); } } if (points == null) { return this.geometryFactory.point(); } else { final int axisCount = points.getAxisCount(); final GeometryFactory geometryFactory = this.geometryFactory.convertAxisCount(axisCount); return geometryFactory.point(points); } } private Polygon parsePolygon() throws XMLStreamException { this.in.requireLocalName(POLYGON); final List<LinearRing> rings = new ArrayList<>(); int axisCount = 2; final int depth = this.in.getDepth(); while (this.in.skipToStartElements(depth, OUTER_BOUNDARY_IS, INNER_BOUNDARY_IS)) { final LinearRing ring = parseRing(); if (ring != null) { axisCount = Math.max(axisCount, ring.getAxisCount()); rings.add(ring); } } final GeometryFactory geometryFactory = this.geometryFactory.convertAxisCount(axisCount); final Polygon polygon = geometryFactory.polygon(rings); return polygon; } private LinearRing parseRing() throws XMLStreamException { final int depth = this.in.getDepth(); while (this.in.skipToStartElements(depth, LINEAR_RING)) { final LinearRing ring = parseLinearRing(); return ring; } return null; } @Override public void remove() { throw new UnsupportedOperationException(); } @Override public String toString() { return this.in.toString(); } }