/** * This file is part of OSM2ShareNav * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as published by * the Free Software Foundation. * * Copyright (C) 2007 Harald Mueller * Copyright (C) 2010 Kai Krueger */ package net.sharenav.osmToShareNav; import java.io.IOException; import java.io.InputStream; import java.util.Hashtable; import java.util.LinkedList; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.helpers.DefaultHandler; import net.sharenav.osmToShareNav.model.Entity; import net.sharenav.osmToShareNav.model.Member; import net.sharenav.osmToShareNav.model.Node; import net.sharenav.osmToShareNav.model.Relation; import net.sharenav.osmToShareNav.model.TurnRestriction; import net.sharenav.osmToShareNav.model.Way; public class OxParser extends OsmParser { private class OsmXmlHandler extends DefaultHandler { private final Hashtable<String, String> tagsCache = new Hashtable<String, String>(); @Override public void startDocument() { System.out.println("Start of Document"); System.out .println("Nodes read/used, Ways read/used, Relations read/partial/used"); } @Override public void endDocument() { long time = (System.currentTimeMillis() - startTime) / 1000; System.out.println("Nodes " + nodeTot + "/" + nodeIns + ", Ways " + wayTot + "/" + wayIns + ", Relations " + relTot + "/" + relPart + "/" + relIns + ", Read " + (forwardInputStream.lBytesRead/1000) + " KiB"); printMemoryUsage(2); System.out.println("End of document, reading took " + time + " seconds"); } @Override public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { // System.out.println("start " + localName + " " + qName); if (qName.equals("node")) { nodeTot++; float node_lat = Float.parseFloat(atts.getValue("lat")); float node_lon = Float.parseFloat(atts.getValue("lon")); if (nodeInArea(node_lat, node_lon)) { long id = Long.parseLong(atts.getValue("id")); current = new Node(node_lat, node_lon, id); } else { current = null; } } if (qName.equals("way")) { long id = Long.parseLong(atts.getValue("id")); current = new Way(id); ((Way) current).used = true; } if (qName.equals("nd")) { if (current instanceof Way) { Way way = ((Way) current); long ref = Long.parseLong(atts.getValue("ref")); Node node = nodes.get(new Long(ref)); if (node != null) { way.addNode(node); } else { // Node for this Way is missing, problem in OS or simply // out of Bounding Box // tree different cases are possible // missing at the start, in the middle or at the end // we simply add the current way and start a new one // with shared attributes. // degenerate ways are not added, so don't care about // this here. if (way.getNodeCount() != 0) { /** * Attributes might not be fully known yet, so keep * track of which ways are duplicates and clone the * tags once the XML for this way is fully parsed */ if (duplicateWays == null) { duplicateWays = new LinkedList<Way>(); } duplicateWays.add(way); current = new Way(way); } } } } if (qName.equals("tag")) { if (current != null) { String key = atts.getValue("k"); String val = atts.getValue("v"); /** * atts.getValue creates a new String every time If we store * key and val directly in current.setAttribute we end up * having thousands of duplicate Strings for e.g. "name" and * "highway". By filtering it through a Hashtable we can * reuse the String objects thereby saving a significant * amount of memory. */ if (key != null && val != null) { /** * Filter out common tags that are definitely not used * such as created_by If this is the only tag on a Node, * we end up saving creating a Hashtable object to store * the tags, saving some memory. */ if (LegendParser.getRelevantKeys().contains(key)) { if (!tagsCache.containsKey(key)) { tagsCache.put(key, key); } if (!tagsCache.containsKey(val)) { tagsCache.put(val, val); } current.setAttribute(tagsCache.get(key), tagsCache .get(val)); } } } } if (qName.equals("relation")) { long id = Long.parseLong(atts.getValue("id")); current = new Relation(id); } if (qName.equals("member")) { if (current instanceof Relation) { Relation r = (Relation) current; Member m = new Member(atts.getValue("type"), atts .getValue("ref"), atts.getValue("role")); switch (m.getType()) { case Member.TYPE_NODE: { if (!nodes.containsKey(new Long(m.getRef()))) { r.setPartial(); return; } break; } case Member.TYPE_WAY: { if (!ways.containsKey(new Long(m.getRef()))) { r.setPartial(); return; } break; } case Member.TYPE_RELATION: { if (m.getRef() > r.id) { // We haven't parsed this relation yet, so // we have to assume it is valid for the moment } else { if (!relations.containsKey(new Long(m.getRef()))) { r.setPartial(); return; } } break; } } r.add(m); } } } // startElement @Override public void endElement(String namespaceURI, String localName, String qName) { // System.out.println("end " + localName + " " + qName); ele++; if (System.currentTimeMillis() - lLastStatusTime > 10000) { ele = 0; lLastStatusTime = System.currentTimeMillis(); System.out.println("Nodes " + nodeTot + "/" + nodeIns + ", Ways " + wayTot + "/" + wayIns + ", Relations " + relTot + "/" + relPart + "/" + relIns + ", Read " + (forwardInputStream.lBytesRead/1000) + " KiB"); } if (qName.equals("node")) { if (current == null) { return; // Node not in bound } Node n = (Node) current; previousNodeWithThisId = nodes.put(n.id, n); nodeIns++; if (current.getAttribute("highway") != null && current.getAttribute("highway").equalsIgnoreCase( "traffic_signals")) { // decrement trafficSignalCount if a previous node with this // id got replaced but was a traffic signal node if (previousNodeWithThisId != null && previousNodeWithThisId.isTrafficSignals()) { trafficSignalCount--; System.out.println("DUPLICATE TRAFFIC SIGNAL NODE ID: " + previousNodeWithThisId.id + " more than once in osm file"); } n.markAsTrafficSignals(); trafficSignalCount++; } current = null; } if (qName.equals("way")) { wayTot++; Way w = (Way) current; // TODO: this seems to be not useful, because the list of tags // is shared (only one list) // so a add of an attribute to one if the ways already adds it // to the // other as well. if (duplicateWays != null) { for (Way ww : duplicateWays) { ww.cloneTags(w); addWay(ww); if (ww.isArea()) { ww.saveOutline(); } } duplicateWays = null; } addWay(w); if (w.isArea()) { w.saveOutline(); } current = null; } if (qName.equals("relation")) { relTot++; /** * Store way and node refs temporarily in the same variable Way * refs must be resolved later to nodes when we actually know * all the ways */ long viaNodeOrWayRef = 0; Relation r = (Relation) current; if (r.isValid()) { if (!r.isPartial()) { relIns++; viaNodeOrWayRef = r.getViaNodeOrWayRef(); } else { relPart++; } if (viaNodeOrWayRef != 0) { TurnRestriction turnRestriction = new TurnRestriction(r); if (r.isViaWay()) { // Store the ref to the via way turnRestriction.viaWayRef = viaNodeOrWayRef; // add a flag to the turn restriction if it's a way turnRestriction.flags |= TurnRestriction.VIA_TYPE_IS_WAY; // add restrictions with viaWays into an ArrayList // to be resolved later turnRestrictionsWithViaWays.add(turnRestriction); } else { // remember normal turn restrictions now // because we already know the via node addTurnRestriction(viaNodeOrWayRef, turnRestriction); } } else { relations.put(r.id, r); } } current = null; } } // endElement @Override public void fatalError(SAXParseException e) throws SAXException { System.out.println("Error: " + e); throw e; } } /** * This class represents an input stream which counts the bytes read. */ class ForwardInputStream extends InputStream { InputStream inputStream = null; long lBytesRead = 0; public ForwardInputStream(InputStream inputStream) { this.inputStream = inputStream; } @Override public int available() { throw new UnsupportedOperationException("Not supported yet."); } @Override public void close() throws IOException { inputStream.close(); } @Override public void mark(int readlimit) { throw new UnsupportedOperationException("Not supported yet."); } @Override public boolean markSupported() { throw new UnsupportedOperationException("Not supported yet."); } @Override public int read() throws IOException { int readByte = inputStream.read(); if ( readByte != -1 ) lBytesRead++; return readByte; } @Override public int read(byte[] b) throws IOException { int iNumReadBytes = inputStream.read(b); if ( iNumReadBytes != -1 ) lBytesRead+=iNumReadBytes; return iNumReadBytes; } @Override public int read(byte[] b, int off, int len) throws IOException { int iNumReadBytes = inputStream.read(b, off, len); if ( iNumReadBytes != -1 ) lBytesRead+=iNumReadBytes; return iNumReadBytes; } @Override public void reset() { throw new UnsupportedOperationException("Not supported yet."); } @Override public long skip(long n) { throw new UnsupportedOperationException("Not supported yet."); } } /** * The current processed primitive */ private Entity current = null; private int nodeTot, nodeIns; private int wayTot, wayIns; private int ele; private int relTot, relPart, relIns; /** * Keep track of ways that get split, as at the time of splitting not all * tags have been added. So need to add them to all duplicates. */ private LinkedList<Way> duplicateWays; private long startTime; private Node previousNodeWithThisId; private ForwardInputStream forwardInputStream = null; private long lLastStatusTime = 0; /** * @param i * InputStream from which planet file is read */ public OxParser(InputStream i) { super(i); } /** * @param i * InputStream from which planet file is read * @param c * Configuration which supplies the bounds */ public OxParser(InputStream i, Configuration c) { super(i, c); } @Override protected String parserType() { return "Osm XML"; } @Override protected void init(InputStream i) { try { startTime = System.currentTimeMillis(); SAXParserFactory factory = SAXParserFactory.newInstance(); // Parse the input factory.setValidating(false); SAXParser saxParser = factory.newSAXParser(); lLastStatusTime = System.currentTimeMillis(); forwardInputStream = new ForwardInputStream(i); saxParser.parse(forwardInputStream, new OsmXmlHandler()); // parse(new InputStreamReader(new BufferedInputStream(i,10240), // "UTF-8")); } catch (IOException e) { System.out.println("IOException: " + e); e.printStackTrace(); /* * The planet file is presumably corrupt. So there is no point in * continuing, as it will most likely generate incorrect map data. */ System.exit(10); } catch (SAXException e) { System.out.println("SAXException: " + e); e.printStackTrace(); /* * The planet file is presumably corrupt. So there is no point in * continuing, as it will most likely generate incorrect map data. */ System.exit(10); } catch (Exception e) { System.out.println("Other Exception: " + e); e.printStackTrace(); /* * The planet file is presumably corrupt. So there is no point in * continuing, as it will most likely generate incorrect map data. */ System.exit(10); } } }