// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.plugins.opendata.core.io.geographic;
import java.awt.Component;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.opengis.geometry.MismatchedDimensionException;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.NoSuchAuthorityCodeException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
import org.opengis.referencing.operation.TransformException;
import org.openstreetmap.josm.Main;
import org.openstreetmap.josm.data.osm.DataSet;
import org.openstreetmap.josm.data.osm.Node;
import org.openstreetmap.josm.data.osm.OsmPrimitive;
import org.openstreetmap.josm.data.osm.Way;
import org.openstreetmap.josm.gui.progress.ProgressMonitor;
import org.openstreetmap.josm.io.UTFInputStreamReader;
import org.openstreetmap.josm.plugins.opendata.core.OdConstants;
import org.openstreetmap.josm.plugins.opendata.core.datasets.AbstractDataSetHandler;
import org.openstreetmap.josm.plugins.opendata.core.datasets.NationalHandlers;
import org.openstreetmap.josm.tools.UserCancelException;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;
public class GmlReader extends GeographicReader {
public static final String GML_FEATURE_MEMBER = "featureMember";
public static final String GML_LINE_STRING = "LineString";
public static final String GML_LINEAR_RING = "LinearRing";
public static final String GML_POINT = "Point";
public static final String GML_SURFACE = "Surface";
public static final String GML_SRS_NAME = "srsName";
public static final String GML_SRS_DIMENSION = "srsDimension";
public static final String GML_POS_LIST = "posList";
public static final String GML_COORDINATES = "coordinates";
private final GeometryFactory geometryFactory = new GeometryFactory();
private final GmlHandler gmlHandler;
private XMLStreamReader parser;
private int dim;
private final class CrsData {
public CoordinateReferenceSystem crs;
public MathTransform transform;
public int dim;
CrsData(CoordinateReferenceSystem crs, MathTransform transform, int dim) {
this.crs = crs;
this.transform = transform;
this.dim = dim;
}
}
private final Map<String, CrsData> crsDataMap = new HashMap<>();
public GmlReader(XMLStreamReader parser, GmlHandler handler) {
super(handler, NationalHandlers.DEFAULT_GML_HANDLERS);
this.parser = parser;
this.gmlHandler = handler;
}
public static DataSet parseDataSet(InputStream in, AbstractDataSetHandler handler, ProgressMonitor instance)
throws IOException, XMLStreamException {
InputStreamReader ir = UTFInputStreamReader.create(in, OdConstants.UTF8);
XMLStreamReader parser = XMLInputFactory.newInstance().createXMLStreamReader(ir);
try {
return new GmlReader(parser, handler != null ? handler.getGmlHandler() : null).parseDoc(instance);
} catch (Exception e) {
throw new IOException(e);
}
}
private boolean isElement(String element) {
return parser.getLocalName().matches("(gml:)?"+element);
}
private DataSet parseDoc(ProgressMonitor instance) throws XMLStreamException, GeoCrsException, FactoryException,
GeoMathTransformException, MismatchedDimensionException, TransformException {
Component parent = instance != null ? instance.getWindowParent() : Main.parent;
while (parser.hasNext()) {
int event = parser.next();
if (event == XMLStreamConstants.START_ELEMENT) {
if (isElement(GML_FEATURE_MEMBER)) {
try {
parseFeatureMember(parent);
} catch (UserCancelException e) {
return ds;
}
}
}
}
return ds;
}
private void findCRS(String srs) throws NoSuchAuthorityCodeException, FactoryException {
Main.info("Finding CRS for "+srs);
if (gmlHandler != null) {
crs = gmlHandler.getCrsFor(srs);
} else {
for (GmlHandler h : NationalHandlers.DEFAULT_GML_HANDLERS) {
if ((crs = h.getCrsFor(srs)) != null) {
return;
}
}
}
}
private void parseSrs(Component parent) throws GeoCrsException, FactoryException, UserCancelException, GeoMathTransformException {
String srs = parser.getAttributeValue(null, GML_SRS_NAME);
String sdim = parser.getAttributeValue(null, GML_SRS_DIMENSION);
dim = sdim != null ? Integer.parseInt(sdim) : 2;
CrsData crsData = crsDataMap.get(srs);
if (crsData == null) {
try {
findCRS(srs);
} catch (NoSuchAuthorityCodeException e) {
e.printStackTrace();
} catch (FactoryException e) {
e.printStackTrace();
}
if (crs == null) {
throw new GeoCrsException("Unable to detect CRS for srs '"+srs+"' !");
} else {
findMathTransform(parent, false);
}
crsDataMap.put(srs, new CrsData(crs, transform, dim));
} else {
crs = crsData.crs;
transform = crsData.transform;
dim = crsData.dim;
}
}
private void parseFeatureMember(Component parent) throws XMLStreamException, GeoCrsException, FactoryException,
UserCancelException, GeoMathTransformException, MismatchedDimensionException, TransformException {
Way way = null;
Node node = null;
Map<String, StringBuilder> tags = new HashMap<>();
OsmPrimitive prim = null;
String key = null;
while (parser.hasNext()) {
int event = parser.next();
if (event == XMLStreamConstants.START_ELEMENT) {
if (isElement(GML_LINE_STRING)) {
prim = way = createWay();
parseSrs(parent);
} else if (isElement(GML_LINEAR_RING)) {
prim = way = createWay();
} else if (isElement(GML_POINT)) {
parseSrs(parent);
} else if (isElement(GML_SURFACE)) {
parseSrs(parent);
} else if (isElement(GML_POS_LIST)) {
String[] tab = parser.getElementText().split(" ");
for (int i = 0; i < tab.length; i += dim) {
Point p = geometryFactory.createPoint(new Coordinate(Double.valueOf(tab[i]), Double.valueOf(tab[i+1])));
node = createOrGetNode(p, dim > 2 && !tab[i+2].equals("0") ? tab[i+2] : null);
if (way != null) {
way.addNode(node);
} else {
prim = node;
}
}
} else if (isElement(GML_COORDINATES)) {
String[] tab = parser.getElementText().trim().split(",");
Point p = geometryFactory.createPoint(new Coordinate(Double.valueOf(tab[0]), Double.valueOf(tab[1])));
node = createOrGetNode(p);
if (way == null) {
prim = node;
}
} else {
key = parser.getLocalName();
if (key.startsWith("ogr:")) {
key = key.substring("ogr:".length());
}
}
} else if (event == XMLStreamConstants.END_ELEMENT) {
if (isElement(GML_FEATURE_MEMBER)) {
break;
}
} else if (event == XMLStreamConstants.CHARACTERS && key != null) {
StringBuilder sb = tags.get(key);
if (sb == null) {
sb = new StringBuilder();
tags.put(key, sb);
}
sb.append(parser.getTextCharacters(), parser.getTextStart(), parser.getTextLength());
}
}
if (prim != null) {
for (String k : tags.keySet()) {
prim.put(k, tags.get(k).toString());
}
}
}
}