/*
* Copyright (C) 2010.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 or
* version 2 as published by the Free Software Foundation.
*
* 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.
*/
package uk.me.parabola.mkgmap.reader.osm.bin;
import java.util.List;
import uk.me.parabola.imgfmt.MapFailedException;
import uk.me.parabola.imgfmt.app.Coord;
import uk.me.parabola.mkgmap.reader.osm.Element;
import uk.me.parabola.mkgmap.reader.osm.GeneralRelation;
import uk.me.parabola.mkgmap.reader.osm.Node;
import uk.me.parabola.mkgmap.reader.osm.OsmHandler;
import uk.me.parabola.mkgmap.reader.osm.Way;
import uk.me.parabola.util.EnhancedProperties;
import crosby.binary.BinaryParser;
import crosby.binary.Osmformat;
/**
* Handler for Scott Crosby's binary format, based on the Google
* protobuf format.
*
* @author Steve Ratcliffe
*/
public class OsmBinHandler extends OsmHandler {
public OsmBinHandler(EnhancedProperties props) {
}
public class BinParser extends BinaryParser {
protected void parse(Osmformat.HeaderBlock header) {
double multiplier = .000000001;
double maxLon = header.getBbox().getRight() * multiplier;
double minLon = header.getBbox().getLeft() * multiplier;
double maxLat = header.getBbox().getTop() * multiplier;
double minLat = header.getBbox().getBottom() * multiplier;
for (String s : header.getRequiredFeaturesList()) {
if (s.equals("OsmSchema-V0.6"))
continue; // We can parse this.
if (s.equals("DenseNodes"))
continue; // We can parse this.
throw new MapFailedException("File requires unknown feature: " + s);
}
setBBox(minLat, minLon, maxLat, maxLon);
}
protected void parseNodes(List<Osmformat.Node> nodes) {
for (Osmformat.Node binNode : nodes) {
Coord co = new Coord(parseLat(binNode.getLat()), parseLon(binNode.getLon()));
long id = binNode.getId();
saver.addPoint(id, co);
int tagCount = binNode.getKeysCount();
if (tagCount > 0) {
Node node = new Node(id, co);
for (int tid = 0; tid < tagCount; tid++) {
String key = getStringById(binNode.getKeys(tid));
String val = getStringById(binNode.getVals(tid));
key = keepTag(key, val);
if (key != null)
node.addTagFromRawOSM(key, val);
}
saver.addNode(node);
hooks.onAddNode(node);
}
}
}
protected final void parseDense(Osmformat.DenseNodes nodes) {
long lastId = 0, lastLat = 0, lastLon = 0;
int kvid = 0; // Index into the key val array.
for (int nid = 0; nid < nodes.getIdCount(); nid++) {
long lat = nodes.getLat(nid) + lastLat;
long lon = nodes.getLon(nid) + lastLon;
long id = nodes.getId(nid) + lastId;
lastLat = lat;
lastLon = lon;
lastId = id;
Coord co = new Coord(parseLat(lat), parseLon(lon));
saver.addPoint(id, co);
if (nodes.getKeysValsCount() > 0) {
int ntags = 0;
Node node = null;
while (nodes.getKeysVals(kvid) != 0) {
int keyid = nodes.getKeysVals(kvid++);
int valid = nodes.getKeysVals(kvid++);
String key = getStringById(keyid);
String val = getStringById(valid);
key = keepTag(key, val);
if (key != null) {
if (node == null)
node = new Node(id, co);
node.addTagFromRawOSM(key, val);
ntags++;
}
}
kvid++; // Skip over the '0' delimiter.
if (ntags > 0) {
// If there are tags, then we save a proper node for it.
saver.addNode(node);
hooks.onAddNode(node);
}
}
}
}
protected void parseWays(List<Osmformat.Way> ways) {
for (Osmformat.Way binWay : ways) {
Way way = startWay(binWay.getId());
for (int j = 0; j < binWay.getKeysCount(); j++) {
String key = getStringById(binWay.getKeys(j));
String val = getStringById(binWay.getVals(j));
key = keepTag(key, val);
if (key != null)
way.addTagFromRawOSM(key, val);
}
long nid = 0;
for (long idDelta : binWay.getRefsList()) {
nid += idDelta;
addCoordToWay(way, nid);
}
endWay(way);
}
}
protected void parseRelations(List<Osmformat.Relation> rels) {
for (Osmformat.Relation binRel : rels) {
long id = binRel.getId();
GeneralRelation rel = new GeneralRelation(id);
boolean tagsIncomplete = false;
for (int j = 0; j < binRel.getKeysCount(); j++) {
String key = getStringById(binRel.getKeys(j));
String val = getStringById(binRel.getVals(j));
// type is required for relations - all other tags are filtered
if ("type".equals(key))
// intern the string
key = "type";
else
key = keepTag(key, val);
if (key == null)
tagsIncomplete = true;
else
rel.addTagFromRawOSM(key, val);
}
if (tagsIncomplete) {
String relType = rel.getTag("type");
if ("multipolygon".equals(relType) || "boundary".equals(relType)) {
// mark the multipolygons if there are some tags that are not loaded
rel.addTag(TAGS_INCOMPLETE_TAG, "true");
}
}
long lastMid = 0;
for (int j = 0; j < binRel.getMemidsCount(); j++) {
long mid = lastMid + binRel.getMemids(j);
lastMid = mid;
String role = getStringById(binRel.getRolesSid(j));
Element el = null;
if (binRel.getTypes(j) == Osmformat.Relation.MemberType.NODE) {
el = saver.getNode(mid);
if(el == null) {
// we didn't make a node for this point earlier,
// do it now (if it exists)
Coord co = saver.getCoord(mid);
if(co != null) {
el = new Node(mid, co);
saver.addNode((Node)el);
}
}
} else if (binRel.getTypes(j) == Osmformat.Relation.MemberType.WAY) {
el = saver.getWay(mid);
} else if (binRel.getTypes(j) == Osmformat.Relation.MemberType.RELATION) {
el = saver.getRelation(mid);
if (el == null) {
saver.deferRelation(mid, rel, role);
}
} else {
assert false;
}
if (el != null) // ignore non existing ways caused by splitting files
rel.addElement(role, el);
}
saver.addRelation(rel);
}
}
/**
* Called when the file is fully read.
*/
public void complete() {
}
}
}