// This software is released into the Public Domain. See copying.txt for details.
package crosby.binary.osmosis;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.openstreetmap.osmosis.core.OsmosisConstants;
import org.openstreetmap.osmosis.core.OsmosisRuntimeException;
import org.openstreetmap.osmosis.core.container.v0_6.BoundContainer;
import org.openstreetmap.osmosis.core.container.v0_6.NodeContainer;
import org.openstreetmap.osmosis.core.container.v0_6.RelationContainer;
import org.openstreetmap.osmosis.core.container.v0_6.WayContainer;
import org.openstreetmap.osmosis.core.domain.v0_6.Bound;
import org.openstreetmap.osmosis.core.domain.v0_6.CommonEntityData;
import org.openstreetmap.osmosis.core.domain.v0_6.EntityType;
import org.openstreetmap.osmosis.core.domain.v0_6.Node;
import org.openstreetmap.osmosis.core.domain.v0_6.OsmUser;
import org.openstreetmap.osmosis.core.domain.v0_6.Relation;
import org.openstreetmap.osmosis.core.domain.v0_6.RelationMember;
import org.openstreetmap.osmosis.core.domain.v0_6.Tag;
import org.openstreetmap.osmosis.core.domain.v0_6.Way;
import org.openstreetmap.osmosis.core.domain.v0_6.WayNode;
import org.openstreetmap.osmosis.core.task.v0_6.Sink;
import org.openstreetmap.osmosis.osmbinary.BinaryParser;
import org.openstreetmap.osmosis.osmbinary.Osmformat;
import org.openstreetmap.osmosis.osmbinary.Osmformat.DenseInfo;
/** Class that reads and parses binary files and sends the contained entities to the sink. */
public class OsmosisBinaryParser extends BinaryParser {
@Override
public void complete() {
sink.complete();
}
/** Get the osmosis object representing a the user in a given Info protobuf.
* @param info The info protobuf.
* @return The OsmUser object */
OsmUser getUser(Osmformat.Info info) {
// System.out.println(info);
if (info.hasUid() && info.hasUserSid()) {
if (info.getUid() < 0) {
return OsmUser.NONE;
}
return new OsmUser(info.getUid(), getStringById(info.getUserSid()));
} else {
return OsmUser.NONE;
}
}
/** The magic number used to indicate no version number metadata for this entity. */
static final int NOVERSION = -1;
/** The magic number used to indicate no changeset metadata for this entity. */
static final int NOCHANGESET = -1;
@Override
protected void parseNodes(List<Osmformat.Node> nodes) {
for (Osmformat.Node i : nodes) {
List<Tag> tags = new ArrayList<Tag>();
for (int j = 0; j < i.getKeysCount(); j++) {
tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
}
// long id, int version, Date timestamp, OsmUser user,
// long changesetId, Collection<Tag> tags,
// double latitude, double longitude
Node tmp;
long id = i.getId();
double latf = parseLat(i.getLat()), lonf = parseLon(i.getLon());
if (i.hasInfo()) {
Osmformat.Info info = i.getInfo();
tmp = new Node(new CommonEntityData(id, info.getVersion(), getDate(info),
getUser(info), info.getChangeset(), tags), latf, lonf);
} else {
tmp = new Node(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
NOCHANGESET, tags), latf, lonf);
}
sink.process(new NodeContainer(tmp));
}
}
@Override
protected void parseDense(Osmformat.DenseNodes nodes) {
long lastId = 0, lastLat = 0, lastLon = 0;
int j = 0; // Index into the keysvals array.
// Stuff for dense info
long lasttimestamp = 0, lastchangeset = 0;
int lastuserSid = 0, lastuid = 0;
DenseInfo di = null;
if (nodes.hasDenseinfo()) {
di = nodes.getDenseinfo();
}
for (int i = 0; i < nodes.getIdCount(); i++) {
Node tmp;
List<Tag> tags = new ArrayList<Tag>(0);
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);
// 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++);
tags.add(new Tag(getStringById(keyid), getStringById(valid)));
}
j++; // Skip over the '0' delimiter.
}
// Handle dense info.
if (di != null) {
int uid = di.getUid(i) + lastuid; lastuid = uid;
int userSid = di.getUserSid(i) + lastuserSid; lastuserSid = userSid;
long timestamp = di.getTimestamp(i) + lasttimestamp; lasttimestamp = timestamp;
int version = di.getVersion(i);
long changeset = di.getChangeset(i) + lastchangeset; lastchangeset = changeset;
Date date = new Date(date_granularity * timestamp);
OsmUser user;
if (uid < 0) {
user = OsmUser.NONE;
} else {
user = new OsmUser(uid, getStringById(userSid));
}
tmp = new Node(new CommonEntityData(id, version, date, user, changeset, tags), latf, lonf);
} else {
tmp = new Node(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
NOCHANGESET, tags), latf, lonf);
}
sink.process(new NodeContainer(tmp));
}
}
@Override
protected void parseWays(List<Osmformat.Way> ways) {
for (Osmformat.Way i : ways) {
List<Tag> tags = new ArrayList<Tag>();
for (int j = 0; j < i.getKeysCount(); j++) {
tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
}
long lastId = 0;
List<WayNode> nodes = new ArrayList<WayNode>();
for (long j : i.getRefsList()) {
nodes.add(new WayNode(j + lastId));
lastId = j + lastId;
}
long id = i.getId();
// long id, int version, Date timestamp, OsmUser user,
// long changesetId, Collection<Tag> tags,
// List<WayNode> wayNodes
Way tmp;
if (i.hasInfo()) {
Osmformat.Info info = i.getInfo();
tmp = new Way(new CommonEntityData(id, info.getVersion(), getDate(info),
getUser(info), info.getChangeset(), tags), nodes);
} else {
tmp = new Way(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE, NOCHANGESET,
tags), nodes);
}
sink.process(new WayContainer(tmp));
}
}
@Override
protected void parseRelations(List<Osmformat.Relation> rels) {
for (Osmformat.Relation i : rels) {
List<Tag> tags = new ArrayList<Tag>();
for (int j = 0; j < i.getKeysCount(); j++) {
tags.add(new Tag(getStringById(i.getKeys(j)), getStringById(i.getVals(j))));
}
long id = i.getId();
long lastMid = 0;
List<RelationMember> nodes = new ArrayList<RelationMember>();
for (int j = 0; j < i.getMemidsCount(); j++) {
long mid = lastMid + i.getMemids(j);
lastMid = mid;
String role = getStringById(i.getRolesSid(j));
EntityType etype = null;
if (i.getTypes(j) == Osmformat.Relation.MemberType.NODE) {
etype = EntityType.Node;
} else if (i.getTypes(j) == Osmformat.Relation.MemberType.WAY) {
etype = EntityType.Way;
} else if (i.getTypes(j) == Osmformat.Relation.MemberType.RELATION) {
etype = EntityType.Relation;
} else {
assert false; // TODO; Illegal file?
}
nodes.add(new RelationMember(mid, etype, role));
}
// long id, int version, TimestampContainer timestampContainer,
// OsmUser user,
// long changesetId, Collection<Tag> tags,
// List<RelationMember> members
Relation tmp;
if (i.hasInfo()) {
Osmformat.Info info = i.getInfo();
tmp = new Relation(new CommonEntityData(id, info.getVersion(), getDate(info),
getUser(info), info.getChangeset(), tags), nodes);
} else {
tmp = new Relation(new CommonEntityData(id, NOVERSION, NODATE, OsmUser.NONE,
NOCHANGESET, tags), nodes);
}
sink.process(new RelationContainer(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 OsmosisRuntimeException("File requires unknown feature: " + s);
}
if (block.hasBbox()) {
String source = OsmosisConstants.VERSION;
if (block.hasSource()) {
source = block.getSource();
}
double multiplier = .000000001;
double rightf = block.getBbox().getRight() * multiplier;
double leftf = block.getBbox().getLeft() * multiplier;
double topf = block.getBbox().getTop() * multiplier;
double bottomf = block.getBbox().getBottom() * multiplier;
Bound bounds = new Bound(rightf, leftf, topf, bottomf, source);
sink.process(new BoundContainer(bounds));
}
}
/**
* {@inheritDoc}
*/
public void setSink(Sink sink) {
this.sink = sink;
}
private Sink sink;
}