/* This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.opentripplanner.openstreetmap.impl; import org.opentripplanner.openstreetmap.services.OpenStreetMapContentHandler; import org.opentripplanner.openstreetmap.model.*; import java.util.HashMap; import java.util.List; import java.util.Map; import crosby.binary.BinaryParser; import crosby.binary.Osmformat; /** * Parser for the OpenStreetMap PBF Format. * * @since 0.4 */ public class BinaryOpenStreetMapParser extends BinaryParser { private OpenStreetMapContentHandler _handler; private boolean _parseWays = true; private boolean _parseRelations = true; private boolean _parseNodes = true; private Map<String, String> stringTable = new HashMap<String, String>(); public BinaryOpenStreetMapParser(OpenStreetMapContentHandler handler) { _handler = handler; } // The strings are already being pulled from a string table in the PBF file, // but there appears to be a separate string table per 8k-entry PBF file block. // String.intern grinds to a halt on large PBF files (as it did on GTFS import), so // we implement our own. public String internalize(String s) { String fromTable = stringTable.get(s); if (fromTable == null) { stringTable.put(s, s); return s; } return fromTable; } public void complete() { // Jump in circles } @Override protected void parseNodes(List<Osmformat.Node> nodes) { if(!_parseNodes) { return; } for (Osmformat.Node i : nodes) { OSMNode tmp = new OSMNode(); tmp.setId(i.getId()); tmp.setLat(parseLat(i.getLat())); tmp.setLon(parseLon(i.getLon())); for (int j = 0; j < i.getKeysCount(); j++) { String key = internalize(getStringById(i.getKeys(j))); // if _handler.retain_tag(key) // TODO: filter tags String value = internalize(getStringById(i.getVals(j))); OSMTag tag = new OSMTag(); tag.setK(key); tag.setV(value); tmp.addTag(tag); } _handler.addNode(tmp); } } @Override protected void parseDense(Osmformat.DenseNodes nodes) { long lastId = 0, lastLat = 0, lastLon = 0; int j = 0; // Index into the keysvals array. if(!_parseNodes) { return; } for (int i = 0; i < nodes.getIdCount(); i++) { OSMNode tmp = new OSMNode(); long lat = nodes.getLat(i) + lastLat; lastLat = lat; long lon = nodes.getLon(i) + lastLon; lastLon = lon; long id = nodes.getId(i) + lastId; lastId = id; double latf = parseLat(lat), lonf = parseLon(lon); tmp.setId(id); tmp.setLat(latf); tmp.setLon(lonf); // If empty, assume that nothing here has keys or vals. if (nodes.getKeysValsCount() > 0) { while (nodes.getKeysVals(j) != 0) { int keyid = nodes.getKeysVals(j++); int valid = nodes.getKeysVals(j++); OSMTag tag = new OSMTag(); String key = internalize(getStringById(keyid)); String value = internalize(getStringById(valid)); tag.setK(key); tag.setV(value); tmp.addTag(tag); } j++; // Skip over the '0' delimiter. } _handler.addNode(tmp); } } @Override protected void parseWays(List<Osmformat.Way> ways) { if(!_parseWays) { return; } for (Osmformat.Way i : ways) { OSMWay tmp = new OSMWay(); tmp.setId(i.getId()); for (int j = 0; j < i.getKeysCount(); j++) { OSMTag tag = new OSMTag(); String key = internalize(getStringById(i.getKeys(j))); String value = internalize(getStringById(i.getVals(j))); tag.setK(key); tag.setV(value); tmp.addTag(tag); } long lastId = 0; for (long j : i.getRefsList()) { OSMNodeRef nodeRef = new OSMNodeRef(); nodeRef.setRef(j + lastId); tmp.addNodeRef(nodeRef); lastId = j + lastId; } _handler.addWay(tmp); } } @Override protected void parseRelations(List<Osmformat.Relation> rels) { if(!_parseRelations) { return; } for (Osmformat.Relation i : rels) { OSMRelation tmp = new OSMRelation(); tmp.setId(i.getId()); for (int j = 0; j < i.getKeysCount(); j++) { OSMTag tag = new OSMTag(); String key = internalize(getStringById(i.getKeys(j))); String value = internalize(getStringById(i.getVals(j))); tag.setK(key); tag.setV(value); tmp.addTag(tag); } long lastMid = 0; for (int j = 0; j < i.getMemidsCount(); j++) { OSMRelationMember relMember = new OSMRelationMember(); long mid = lastMid + i.getMemids(j); relMember.setRef(mid); lastMid = mid; relMember.setRole(internalize(getStringById(i.getRolesSid(j)))); if (i.getTypes(j) == Osmformat.Relation.MemberType.NODE) { relMember.setType("node"); } else if (i.getTypes(j) == Osmformat.Relation.MemberType.WAY) { relMember.setType("way"); } else if (i.getTypes(j) == Osmformat.Relation.MemberType.RELATION) { relMember.setType("relation"); } else { assert false; // TODO; Illegal file? } tmp.addMember(relMember); } _handler.addRelation(tmp); } } @Override public void parse(Osmformat.HeaderBlock block) { for (String s : block.getRequiredFeaturesList()) { if (s.equals("OsmSchema-V0.6")) { continue; // We can parse this. } if (s.equals("DenseNodes")) { continue; // We can parse this. } throw new IllegalStateException("File requires unknown feature: " + s); } } /** * Should relations be parsed * * @see org.opentripplanner.graph_builder.services/.sm.OpenStreetMapContentHandler#triPhase */ public void setParseWays(boolean parseWays) { this._parseWays = parseWays; } /** * Should relations be parsed * * @see org.opentripplanner.graph_builder.services/.sm.OpenStreetMapContentHandler#triPhase */ public void setParseRelations(boolean parseRelations) { this._parseRelations = parseRelations; } /** * Should nodes be parsed * * @see org.opentripplanner.graph_builder.services/.sm.OpenStreetMapContentHandler#triPhase */ public void setParseNodes(boolean parseNodes) { _parseNodes = parseNodes; } }