OsmXmlReader.java
package nl.tudelft.simulation.dsol.animation.gis.osm;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.GZIPInputStream;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream;
/**
* OsmXmlReader reads from an OSM XML stream. The stream may be gzip or bzip2 compressed. The reader uses StAX to parse, and
* works in two passes.
* <p>
* Copyright (c) 2025-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
* for project information <a href="https://simulation.tudelft.nl/" target="_blank"> https://simulation.tudelft.nl</a>. The DSOL
* project is distributed under a three-clause BSD-style license, which can be found at
* <a href="https://https://simulation.tudelft.nl/dsol/docs/latest/license.html" target="_blank">
* https://https://simulation.tudelft.nl/dsol/docs/latest/license.html</a>.
* </p>
* @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
*/
public class OsmXmlReader
{
/**
* Create an XML reader.
* @param inputFile the input file
* @param processor the entity processor
* @param format the format with compression
* @throws IOException on I/O error
* @throws XMLStreamException on XML read error
*/
public OsmXmlReader(final File inputFile, final OsmEntityProcessor processor, final OsmFormat format)
throws IOException, XMLStreamException
{
XMLInputFactory factory = XMLInputFactory.newInstance();
// First pass: read the nodes
FileInputStream fileInputStream = new FileInputStream(inputFile);
InputStreamReader inputStreamReader = null;
if (format.equals(OsmFormat.GZIP))
{
GZIPInputStream gzipInputStream = new GZIPInputStream(new BufferedInputStream(fileInputStream));
inputStreamReader = new InputStreamReader(gzipInputStream);
}
else if (format.equals(OsmFormat.BZIP2))
{
BZip2CompressorInputStream bzipInputStream =
new BZip2CompressorInputStream(new BufferedInputStream(fileInputStream));
inputStreamReader = new InputStreamReader(bzipInputStream);
}
else if (format.equals(OsmFormat.OSM))
{
inputStreamReader = new InputStreamReader(new BufferedInputStream(fileInputStream));
}
else
{
throw new IOException("OsmXmlReader called with wrong format: " + format);
}
XMLStreamReader reader = factory.createXMLStreamReader(inputStreamReader);
while (reader.hasNext())
{
int event = reader.next();
if (event == XMLStreamConstants.START_ELEMENT && reader.getLocalName().equals("node"))
{
long id = Long.parseLong(reader.getAttributeValue(null, "id"));
double lat = Double.parseDouble(reader.getAttributeValue(null, "lat"));
double lon = Double.parseDouble(reader.getAttributeValue(null, "lon"));
Node node = new Node(id, lat, lon);
processor.process(node);
}
}
reader.close();
// Second pass: read the ways
fileInputStream = new FileInputStream(inputFile);
inputStreamReader = null;
if (format.equals(OsmFormat.GZIP))
{
GZIPInputStream gzipInputStream = new GZIPInputStream(new BufferedInputStream(fileInputStream));
inputStreamReader = new InputStreamReader(gzipInputStream);
}
else if (format.equals(OsmFormat.BZIP2))
{
BZip2CompressorInputStream bzipInputStream =
new BZip2CompressorInputStream(new BufferedInputStream(fileInputStream));
inputStreamReader = new InputStreamReader(bzipInputStream);
}
else if (format.equals(OsmFormat.OSM))
{
inputStreamReader = new InputStreamReader(new BufferedInputStream(fileInputStream));
}
reader = factory.createXMLStreamReader(inputStreamReader);
boolean inWay = false;
List<Long> ndRefs = new ArrayList<>();
long wayId = 0;
Relation currentRelation = null;
Map<String, String> tags = new HashMap<>();
while (reader.hasNext())
{
int event = reader.next();
if (event == XMLStreamConstants.START_ELEMENT)
{
String name = reader.getLocalName();
if (name.equals("way"))
{
inWay = true;
ndRefs.clear();
tags.clear();
wayId = Long.parseLong(reader.getAttributeValue(null, "id"));
}
else if (inWay && name.equals("nd"))
{
long ref = Long.parseLong(reader.getAttributeValue(null, "ref"));
ndRefs.add(ref);
}
else if (inWay && name.equals("tag"))
{
String k = reader.getAttributeValue(null, "k");
String v = reader.getAttributeValue(null, "v");
tags.put(k, v);
}
else if (name.equals("relation"))
{
long id = Long.parseLong(reader.getAttributeValue(null, "id"));
currentRelation = new Relation(id);
}
else if (name.equals("member") && currentRelation != null)
{
long ref = Long.parseLong(reader.getAttributeValue(null, "ref"));
String role = reader.getAttributeValue(null, "role");
String typeStr = reader.getAttributeValue(null, "type");
Relation.Type type = Relation.Type.valueOf(typeStr.toUpperCase());
Relation.Member member = new Relation.Member(type, ref, role);
currentRelation.addMember(member);
}
else if (name.equals("tag") && currentRelation != null)
{
currentRelation.addTag(reader.getAttributeValue(null, "k"), reader.getAttributeValue(null, "v"));
}
}
if (event == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("way"))
{
inWay = false;
Way w = new Way(wayId, ndRefs, tags);
processor.process(w);
}
if (event == XMLStreamConstants.END_ELEMENT && reader.getLocalName().equals("relation"))
{
processor.process(currentRelation);
currentRelation = null;
}
}
}
}