package com.revolsys.record.io.format.gml;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.regex.Pattern;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamException;
import com.revolsys.collection.iterator.AbstractIterator;
import com.revolsys.geometry.io.GeometryReader;
import com.revolsys.geometry.model.ClockDirection;
import com.revolsys.geometry.model.Geometry;
import com.revolsys.geometry.model.GeometryFactory;
import com.revolsys.geometry.model.LineString;
import com.revolsys.geometry.model.Lineal;
import com.revolsys.geometry.model.LinearRing;
import com.revolsys.geometry.model.Point;
import com.revolsys.geometry.model.Polygon;
import com.revolsys.geometry.model.Polygonal;
import com.revolsys.geometry.model.Punctual;
import com.revolsys.geometry.model.impl.LineStringDouble;
import com.revolsys.io.IoConstants;
import com.revolsys.record.io.format.xml.StaxReader;
import com.revolsys.spring.resource.Resource;
import com.revolsys.util.MathUtil;
public class GmlGeometryReader extends AbstractIterator<Geometry> implements GeometryReader {
public static final LineString parse(final String value, final String separator,
final int axisCount) {
final String[] values = value.split(separator);
final double[] coordinates = new double[values.length];
for (int i = 0; i < values.length; i++) {
final String string = values[i];
coordinates[i] = Double.parseDouble(string);
}
return new LineStringDouble(axisCount, coordinates);
}
public static LineString parse(final String value, final String decimal, String coordSeperator,
String toupleSeperator) {
toupleSeperator = toupleSeperator.replaceAll("\\\\", "\\\\\\\\");
toupleSeperator = toupleSeperator.replaceAll("\\.", "\\\\.");
final Pattern touplePattern = Pattern.compile("\\s*" + toupleSeperator + "\\s*");
final String[] touples = touplePattern.split(value);
coordSeperator = coordSeperator.replaceAll("\\\\", "\\\\\\\\");
coordSeperator = coordSeperator.replaceAll("\\.", "\\\\.");
final Pattern coordinatePattern = Pattern.compile("\\s*" + coordSeperator + "\\s*");
int axisCount = 0;
final List<double[]> listOfCoordinateArrays = new ArrayList<>();
if (touples.length == 0) {
return null;
} else {
for (final String touple : touples) {
final String[] values = coordinatePattern.split(touple);
if (values.length > 0) {
final double[] coordinates = MathUtil.toDoubleArray(values);
axisCount = Math.max(axisCount, coordinates.length);
listOfCoordinateArrays.add(coordinates);
}
}
}
return toCoordinateList(axisCount, listOfCoordinateArrays);
}
public static LineString toCoordinateList(final int axisCount,
final List<double[]> listOfCoordinateArrays) {
final int vertexCount = listOfCoordinateArrays.size();
final double[] coordinates = new double[vertexCount * axisCount];
for (int i = 0; i < vertexCount; i++) {
final double[] coordinates2 = listOfCoordinateArrays.get(i);
for (int j = 0; j < axisCount; j++) {
final double value;
if (j < coordinates2.length) {
value = coordinates2[j];
} else {
value = Double.NaN;
}
coordinates[i * axisCount + j] = value;
}
}
return new LineStringDouble(axisCount, coordinates);
}
private GeometryFactory geometryFactory;
private StaxReader in;
public GmlGeometryReader(final Resource resource) {
try {
this.in = StaxReader.newXmlReader(resource);
} catch (final Exception e) {
throw new IllegalArgumentException("Unable to open resource " + resource);
}
}
@Override
protected void closeDo() {
if (this.in != null) {
this.in.close();
}
this.geometryFactory = null;
this.in = null;
}
@Override
public GeometryFactory getGeometryFactory() {
return this.geometryFactory;
}
private GeometryFactory getGeometryFactory(final GeometryFactory geometryFactory) {
final String srsName = this.in.getAttributeValue(Gml.SRS_NAME.getNamespaceURI(),
Gml.SRS_NAME.getLocalPart());
if (srsName == null) {
return geometryFactory;
} else {
if (srsName.startsWith("urn:ogc:def:crs:EPSG:6.6:")) {
final int srid = Integer.parseInt(srsName.substring("urn:ogc:def:crs:EPSG:6.6:".length()));
final GeometryFactory factory = GeometryFactory.floating3(srid);
return factory;
} else if (srsName.startsWith("EPSG:")) {
final int srid = Integer.parseInt(srsName.substring("EPSG:".length()));
final GeometryFactory factory = GeometryFactory.floating3(srid);
return factory;
} else {
return geometryFactory;
}
}
}
@Override
protected Geometry getNext() {
try {
final int depth = 0;
while (this.in.skipToStartElements(depth, Gml.ENVELOPE_AND_GEOMETRY_TYPE_NAMES)) {
final QName name = this.in.getName();
if (name.equals(Gml.ENVELOPE)) {
this.geometryFactory = getGeometryFactory(this.geometryFactory);
} else {
return readGeometry(this.geometryFactory);
}
}
throw new NoSuchElementException();
} catch (final XMLStreamException e) {
throw new RuntimeException("Error reading next geometry", e);
}
}
@Override
public ClockDirection getPolygonRingDirection() {
return ClockDirection.COUNTER_CLOCKWISE;
}
@Override
protected void initDo() {
this.geometryFactory = getProperty(IoConstants.GEOMETRY_FACTORY);
if (this.geometryFactory == null) {
this.geometryFactory = GeometryFactory.DEFAULT_3D;
}
}
private LineString readCoordinates() throws XMLStreamException {
String decimal = this.in.getAttributeValue(null, "decimal");
if (decimal == null) {
decimal = ".";
}
String coordSeperator = this.in.getAttributeValue(null, "coordSeperator");
if (coordSeperator == null) {
coordSeperator = ",";
}
String toupleSeperator = this.in.getAttributeValue(null, "toupleSeperator");
if (toupleSeperator == null) {
toupleSeperator = " ";
}
final String value = this.in.getElementText();
final LineString points = GmlGeometryReader.parse(value, decimal, coordSeperator,
toupleSeperator);
this.in.skipToEndElement();
return points;
}
private Geometry readGeometry(final GeometryFactory geometryFactory) throws XMLStreamException {
final QName typeName = this.in.getName();
if (typeName.equals(Gml.POINT)) {
return readPoint(geometryFactory);
} else if (typeName.equals(Gml.LINE_STRING)) {
return readLineString(geometryFactory);
} else if (typeName.equals(Gml.POLYGON)) {
return readPolygon(geometryFactory);
} else if (typeName.equals(Gml.MULTI_POINT)) {
return readMultiPoint(geometryFactory);
} else if (typeName.equals(Gml.MULTI_LINE_STRING)) {
return readMultiLineString(geometryFactory);
} else if (typeName.equals(Gml.MULTI_POLYGON)) {
return readMultiPolygon(geometryFactory);
} else if (typeName.equals(Gml.MULTI_GEOMETRY)) {
return readMultiGeometry(geometryFactory);
} else {
throw new IllegalStateException("Unexpected geometry type " + typeName);
}
}
private LinearRing readLinearRing(final GeometryFactory geometryFactory)
throws XMLStreamException {
final GeometryFactory factory = getGeometryFactory(geometryFactory);
LineString points = null;
final int depth = this.in.getDepth();
while (this.in.skipToStartElements(depth, Gml.POS_LIST, Gml.COORDINATES)) {
final QName elementName = this.in.getName();
if (elementName.equals(Gml.POS_LIST)) {
points = readPosList();
} else if (elementName.equals(Gml.COORDINATES)) {
points = readCoordinates();
}
}
if (points == null) {
return factory.linearRing();
} else {
final int axisCount = points.getAxisCount();
return factory.convertAxisCount(axisCount).linearRing(points);
}
}
private LineString readLineString(final GeometryFactory geometryFactory)
throws XMLStreamException {
final GeometryFactory factory = getGeometryFactory(geometryFactory);
LineString points = null;
final int depth = this.in.getDepth();
while (this.in.skipToStartElements(depth, Gml.POS_LIST, Gml.COORDINATES)) {
if (this.in.getName().equals(Gml.POS)) {
points = readPosList();
} else if (this.in.getName().equals(Gml.COORDINATES)) {
points = readCoordinates();
}
}
if (points == null) {
return factory.lineString();
} else {
final int axisCount = points.getAxisCount();
return factory.convertAxisCount(axisCount).lineString(points);
}
}
private Geometry readMultiGeometry(final GeometryFactory geometryFactory)
throws XMLStreamException {
final GeometryFactory factory = getGeometryFactory(geometryFactory);
final List<Geometry> geometries = new ArrayList<>();
this.in.skipSubTree();
return factory.geometry(geometries);
}
private Lineal readMultiLineString(final GeometryFactory geometryFactory)
throws XMLStreamException {
final GeometryFactory factory = getGeometryFactory(geometryFactory);
int axisCount = 2;
final List<LineString> lines = new ArrayList<>();
final int depth = this.in.getDepth();
while (this.in.skipToStartElements(depth, Gml.LINE_STRING)) {
final LineString line = readLineString(factory);
if (line != null) {
axisCount = Math.max(axisCount, line.getAxisCount());
lines.add(line);
}
}
return factory.convertAxisCount(axisCount).lineal(lines);
}
private Punctual readMultiPoint(final GeometryFactory geometryFactory) throws XMLStreamException {
int axisCount = 2;
final List<Point> points = new ArrayList<>();
final GeometryFactory factory = getGeometryFactory(geometryFactory);
final int depth = this.in.getDepth();
while (this.in.skipToStartElements(depth, Gml.POINT)) {
final Point point = readPoint(factory);
if (point != null) {
axisCount = Math.max(axisCount, point.getAxisCount());
points.add(point);
}
}
return factory.convertAxisCount(axisCount).punctual(points);
}
private Polygonal readMultiPolygon(final GeometryFactory geometryFactory)
throws XMLStreamException {
int axisCount = 2;
final GeometryFactory factory = getGeometryFactory(geometryFactory);
final List<Polygon> polygons = new ArrayList<>();
final int depth = this.in.getDepth();
while (this.in.skipToStartElements(depth, Gml.POLYGON)) {
final Polygon polygon = readPolygon(factory);
if (polygon != null) {
axisCount = Math.max(axisCount, polygon.getAxisCount());
polygons.add(polygon);
}
}
return factory.convertAxisCount(axisCount).polygonal(polygons);
}
private Point readPoint(final GeometryFactory geometryFactory) throws XMLStreamException {
final GeometryFactory factory = getGeometryFactory(geometryFactory);
LineString points = null;
final int depth = this.in.getDepth();
while (this.in.skipToStartElements(depth, Gml.POS, Gml.COORDINATES)) {
if (this.in.getName().equals(Gml.POS)) {
points = readPosList();
} else if (this.in.getName().equals(Gml.COORDINATES)) {
points = readCoordinates();
}
}
if (points == null) {
return factory.point();
} else {
final int axisCount = points.getAxisCount();
return factory.convertAxisCount(axisCount).point(points);
}
}
private Polygon readPolygon(final GeometryFactory geometryFactory) throws XMLStreamException {
int axisCount = 2;
final GeometryFactory factory = getGeometryFactory(geometryFactory);
final List<LinearRing> rings = new ArrayList<>();
final int depth = this.in.getDepth();
while (this.in.skipToStartElements(depth, Gml.OUTER_BOUNDARY_IS, Gml.INNER_BOUNDARY_IS)) {
final LinearRing ring = readLinearRing(factory);
if (ring != null) {
axisCount = Math.max(axisCount, ring.getAxisCount());
rings.add(ring);
}
}
final Polygon polygon = factory.convertAxisCount(axisCount).polygon(rings);
return polygon;
}
private LineString readPosList() throws XMLStreamException {
final String dimension = this.in.getAttributeValue(null, "dimension");
if (dimension == null) {
this.in.skipSubTree();
return null;
} else {
final int axisCount = Integer.parseInt(dimension);
final String value = this.in.getElementText();
final LineString points = GmlGeometryReader.parse(value, "\\s+", axisCount);
this.in.skipToEndElement();
return points;
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}