2010-10-27 12:36:07 +02:00
|
|
|
package net.osmand.binary;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
2010-10-27 22:04:09 +02:00
|
|
|
import java.io.RandomAccessFile;
|
2010-10-27 17:18:28 +02:00
|
|
|
import java.util.LinkedHashMap;
|
|
|
|
import java.util.Map;
|
2010-10-27 12:36:07 +02:00
|
|
|
import java.util.Stack;
|
|
|
|
|
2010-10-27 17:18:28 +02:00
|
|
|
import net.osmand.Algoritms;
|
2010-10-27 12:36:07 +02:00
|
|
|
|
|
|
|
import com.google.protobuf.CodedOutputStream;
|
|
|
|
import com.google.protobuf.WireFormat;
|
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
public class BinaryMapIndexWriter {
|
2010-10-27 12:36:07 +02:00
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
private RandomAccessFile raf;
|
2010-10-27 12:36:07 +02:00
|
|
|
private CodedOutputStream codedOutStream;
|
|
|
|
|
|
|
|
private static class Bounds {
|
|
|
|
public Bounds(int leftX, int rightX, int topY, int bottomY) {
|
|
|
|
super();
|
|
|
|
this.bottomY = bottomY;
|
|
|
|
this.leftX = leftX;
|
|
|
|
this.rightX = rightX;
|
|
|
|
this.topY = topY;
|
|
|
|
}
|
|
|
|
int leftX = 0;
|
|
|
|
int rightX = 0;
|
|
|
|
int topY = 0;
|
|
|
|
int bottomY = 0;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
private Stack<Bounds> stackBounds = new Stack<Bounds>();
|
2010-10-27 17:18:28 +02:00
|
|
|
// needed for map tree
|
|
|
|
private Stack<Long> stackBaseIds = new Stack<Long>();
|
|
|
|
private Stack<Map<String, Integer>> stackStringTable = new Stack<Map<String, Integer>>();
|
2010-10-27 12:36:07 +02:00
|
|
|
|
|
|
|
// internal constants to track state of index writing
|
|
|
|
private Stack<Integer> state = new Stack<Integer>();
|
2010-10-27 22:04:09 +02:00
|
|
|
private Stack<Long> stackSizes = new Stack<Long>();
|
|
|
|
|
2010-10-27 12:36:07 +02:00
|
|
|
private final static int OSMAND_STRUCTURE_INIT = 1;
|
|
|
|
private final static int MAP_INDEX_INIT = 2;
|
|
|
|
private final static int MAP_ROOT_LEVEL_INIT = 3;
|
|
|
|
private final static int MAP_TREE = 4;
|
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
public BinaryMapIndexWriter(RandomAccessFile raf, CodedOutputStream codedOutputStream) throws IOException{
|
|
|
|
this.raf = raf;
|
|
|
|
codedOutStream = codedOutputStream;
|
2010-10-27 12:36:07 +02:00
|
|
|
state.push(OSMAND_STRUCTURE_INIT);
|
|
|
|
}
|
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
private void preserveInt32Size() throws IOException {
|
|
|
|
stackSizes.push(raf.getFilePointer());
|
|
|
|
codedOutStream.writeFixed32NoTag(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void writeInt32Size() throws IOException{
|
|
|
|
long filePointer = raf.getFilePointer();
|
|
|
|
Long old = stackSizes.pop();
|
|
|
|
int length = (int) (filePointer - old);
|
|
|
|
raf.seek(old);
|
|
|
|
raf.writeInt(length);
|
|
|
|
raf.seek(filePointer);
|
|
|
|
}
|
|
|
|
|
2010-10-27 12:36:07 +02:00
|
|
|
public void startWriteMapIndex() throws IOException{
|
|
|
|
assert state.peek() == OSMAND_STRUCTURE_INIT;
|
|
|
|
state.push(MAP_INDEX_INIT);
|
|
|
|
codedOutStream.writeTag(OsmandOdb.OsmAndStructure.MAPINDEX_FIELD_NUMBER, WireFormat.FieldType.MESSAGE.getWireType());
|
2010-10-27 22:04:09 +02:00
|
|
|
preserveInt32Size();
|
2010-10-27 12:36:07 +02:00
|
|
|
}
|
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
|
2010-10-27 12:36:07 +02:00
|
|
|
public void endWriteMapIndex() throws IOException{
|
|
|
|
Integer st = state.pop();
|
|
|
|
assert st == MAP_INDEX_INIT;
|
2010-10-27 22:04:09 +02:00
|
|
|
writeInt32Size();
|
2010-10-27 12:36:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void startWriteMapLevelIndex(int maxZoom, int minZoom, int leftX, int rightX, int topY, int bottomY) throws IOException{
|
|
|
|
assert state.peek() == MAP_INDEX_INIT;
|
|
|
|
state.push(MAP_ROOT_LEVEL_INIT);
|
|
|
|
|
|
|
|
codedOutStream.writeTag(OsmandOdb.OsmAndMapIndex.LEVELS_FIELD_NUMBER, WireFormat.FieldType.MESSAGE.getWireType());
|
2010-10-27 22:04:09 +02:00
|
|
|
preserveInt32Size();
|
2010-10-27 12:36:07 +02:00
|
|
|
|
|
|
|
codedOutStream.writeInt32(OsmandOdb.MapRootLevel.MAXZOOM_FIELD_NUMBER, maxZoom);
|
|
|
|
codedOutStream.writeInt32(OsmandOdb.MapRootLevel.MINZOOM_FIELD_NUMBER, minZoom);
|
|
|
|
codedOutStream.writeInt32(OsmandOdb.MapRootLevel.LEFT_FIELD_NUMBER, leftX);
|
|
|
|
codedOutStream.writeInt32(OsmandOdb.MapRootLevel.RIGHT_FIELD_NUMBER, rightX);
|
|
|
|
codedOutStream.writeInt32(OsmandOdb.MapRootLevel.TOP_FIELD_NUMBER, topY);
|
|
|
|
codedOutStream.writeInt32(OsmandOdb.MapRootLevel.BOTTOM_FIELD_NUMBER, bottomY);
|
|
|
|
|
|
|
|
stackBounds.push(new Bounds(leftX, rightX, topY, bottomY));
|
|
|
|
}
|
|
|
|
|
|
|
|
public void endWriteMapLevelIndex() throws IOException{
|
|
|
|
assert state.peek() == MAP_ROOT_LEVEL_INIT;
|
|
|
|
state.pop();
|
|
|
|
stackBounds.pop();
|
2010-10-27 22:04:09 +02:00
|
|
|
writeInt32Size();
|
2010-10-27 12:36:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void startMapTreeElement(int leftX, int rightX, int topY, int bottomY) throws IOException{
|
|
|
|
assert state.peek() == MAP_ROOT_LEVEL_INIT || state.peek() == MAP_TREE;
|
|
|
|
if(state.peek() == MAP_ROOT_LEVEL_INIT){
|
|
|
|
codedOutStream.writeTag(OsmandOdb.MapRootLevel.ROOT_FIELD_NUMBER, WireFormat.FieldType.MESSAGE.getWireType());
|
|
|
|
} else {
|
|
|
|
codedOutStream.writeTag(OsmandOdb.MapTree.SUBTREES_FIELD_NUMBER, WireFormat.FieldType.MESSAGE.getWireType());
|
|
|
|
}
|
2010-10-27 22:04:09 +02:00
|
|
|
preserveInt32Size();
|
2010-10-27 12:36:07 +02:00
|
|
|
state.push(MAP_TREE);
|
|
|
|
|
|
|
|
|
|
|
|
Bounds bounds = stackBounds.peek();
|
|
|
|
codedOutStream.writeSInt32(OsmandOdb.MapTree.LEFT_FIELD_NUMBER, leftX - bounds.leftX);
|
|
|
|
codedOutStream.writeSInt32(OsmandOdb.MapTree.RIGHT_FIELD_NUMBER, rightX - bounds.rightX);
|
|
|
|
codedOutStream.writeSInt32(OsmandOdb.MapTree.TOP_FIELD_NUMBER, topY - bounds.topY);
|
|
|
|
codedOutStream.writeSInt32(OsmandOdb.MapTree.BOTTOM_FIELD_NUMBER, bottomY - bounds.bottomY);
|
|
|
|
stackBounds.push(new Bounds(leftX, rightX, topY, bottomY));
|
2010-10-27 22:04:09 +02:00
|
|
|
stackBaseIds.push(-1L);
|
2010-10-27 17:18:28 +02:00
|
|
|
stackStringTable.push(null);
|
2010-10-27 12:36:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
public void endWriteMapTreeElement() throws IOException{
|
|
|
|
assert state.peek() == MAP_TREE;
|
|
|
|
state.pop();
|
|
|
|
|
|
|
|
stackBounds.pop();
|
2010-10-27 17:18:28 +02:00
|
|
|
Long l = stackBaseIds.pop();
|
2010-10-27 22:04:09 +02:00
|
|
|
if(l != -1){
|
2010-10-27 17:18:28 +02:00
|
|
|
codedOutStream.writeTag(OsmandOdb.MapTree.BASEID_FIELD_NUMBER, WireFormat.FieldType.UINT64.getWireType());
|
|
|
|
codedOutStream.writeUInt64NoTag(l);
|
|
|
|
}
|
|
|
|
Map<String, Integer> map = stackStringTable.peek();
|
|
|
|
if(map != null){
|
|
|
|
|
|
|
|
int i = 0;
|
2010-10-27 22:04:09 +02:00
|
|
|
int size = 0;
|
2010-10-27 17:18:28 +02:00
|
|
|
for(String s : map.keySet()){
|
|
|
|
Integer integer = map.get(s);
|
|
|
|
if(integer != i){
|
|
|
|
throw new IllegalStateException();
|
|
|
|
}
|
|
|
|
i++;
|
2010-10-27 22:04:09 +02:00
|
|
|
size += CodedOutputStream.computeStringSize(OsmandOdb.StringTable.S_FIELD_NUMBER, s);
|
|
|
|
}
|
|
|
|
codedOutStream.writeTag(OsmandOdb.MapTree.STRINGTABLE_FIELD_NUMBER, WireFormat.FieldType.MESSAGE.getWireType());
|
|
|
|
codedOutStream.writeRawVarint32(size);
|
|
|
|
for(String s : map.keySet()){
|
|
|
|
codedOutStream.writeString(OsmandOdb.StringTable.S_FIELD_NUMBER, s);
|
2010-10-27 17:18:28 +02:00
|
|
|
}
|
|
|
|
}
|
2010-10-27 22:04:09 +02:00
|
|
|
writeInt32Size();
|
2010-10-27 12:36:07 +02:00
|
|
|
}
|
|
|
|
|
2010-10-27 17:18:28 +02:00
|
|
|
public void writeMapData(long id, byte[] nodes, byte[] types, String name, int highwayAttributes, byte[] restrictions) throws IOException{
|
2010-10-27 12:36:07 +02:00
|
|
|
assert state.peek() == MAP_TREE;
|
2010-10-27 17:18:28 +02:00
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
|
|
|
|
Bounds bounds = stackBounds.peek();
|
|
|
|
if(stackBaseIds.peek() == -1){
|
|
|
|
stackBaseIds.pop();
|
|
|
|
stackBaseIds.push(id);
|
2010-10-27 17:18:28 +02:00
|
|
|
}
|
2010-10-27 22:04:09 +02:00
|
|
|
// calculate size
|
|
|
|
int allSize = CodedOutputStream.computeTagSize(OsmandOdb.MapData.COORDINATES_FIELD_NUMBER);
|
|
|
|
int sizeCoordinates = 0;
|
2010-10-27 17:18:28 +02:00
|
|
|
for(int i=0; i< nodes.length / 8; i++){
|
|
|
|
int x = Algoritms.parseIntFromBytes(nodes, i*8) - bounds.leftX;
|
|
|
|
int y = Algoritms.parseIntFromBytes(nodes, i*8 + 4) - bounds.topY;
|
2010-10-27 22:04:09 +02:00
|
|
|
sizeCoordinates += CodedOutputStream.computeInt32SizeNoTag(x);
|
|
|
|
sizeCoordinates += CodedOutputStream.computeInt32SizeNoTag(y);
|
2010-10-27 17:18:28 +02:00
|
|
|
}
|
2010-10-27 22:04:09 +02:00
|
|
|
allSize += sizeCoordinates;
|
|
|
|
|
|
|
|
allSize += CodedOutputStream.computeTagSize(OsmandOdb.MapData.TYPES_FIELD_NUMBER);
|
|
|
|
allSize += CodedOutputStream.computeRawVarint32Size(types.length);
|
|
|
|
allSize += types.length;
|
2010-10-27 17:18:28 +02:00
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
allSize += CodedOutputStream.computeSInt64Size(OsmandOdb.MapData.ID_FIELD_NUMBER, id - stackBaseIds.peek());
|
2010-10-27 17:18:28 +02:00
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
int nameId = 0;
|
2010-10-27 17:18:28 +02:00
|
|
|
if(name != null){
|
|
|
|
if(stackStringTable.peek() == null) {
|
|
|
|
stackStringTable.pop();
|
|
|
|
stackStringTable.push(new LinkedHashMap<String, Integer>());
|
|
|
|
}
|
|
|
|
Map<String, Integer> map = stackStringTable.peek();
|
|
|
|
if(map.containsKey(name)) {
|
2010-10-27 22:04:09 +02:00
|
|
|
nameId = map.get(name);
|
2010-10-27 17:18:28 +02:00
|
|
|
} else {
|
2010-10-27 22:04:09 +02:00
|
|
|
nameId = map.size();
|
|
|
|
map.put(name, nameId);
|
2010-10-27 17:18:28 +02:00
|
|
|
}
|
2010-10-27 22:04:09 +02:00
|
|
|
allSize += CodedOutputStream.computeUInt32Size(OsmandOdb.MapData.STRINGID_FIELD_NUMBER, nameId);
|
2010-10-27 17:18:28 +02:00
|
|
|
}
|
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
int restrictionsSize = 0;
|
2010-10-27 17:18:28 +02:00
|
|
|
if(restrictions.length > 0){
|
2010-10-27 22:04:09 +02:00
|
|
|
allSize += CodedOutputStream.computeTagSize(OsmandOdb.MapData.RESTRICTIONS_FIELD_NUMBER);
|
|
|
|
for (int i = 0; i < restrictions.length / 8; i++) {
|
|
|
|
long l = Algoritms.parseLongFromBytes(restrictions, i * 8) - stackBaseIds.peek();
|
|
|
|
restrictionsSize += CodedOutputStream.computeSInt64SizeNoTag(l);
|
|
|
|
}
|
|
|
|
allSize += CodedOutputStream.computeRawVarint32Size(restrictionsSize);
|
|
|
|
allSize += restrictionsSize;
|
2010-10-27 17:18:28 +02:00
|
|
|
}
|
|
|
|
if(highwayAttributes != 0){
|
2010-10-27 22:04:09 +02:00
|
|
|
allSize += CodedOutputStream.computeInt32Size(OsmandOdb.MapData.HIGHWAYMETA_FIELD_NUMBER, highwayAttributes);
|
2010-10-27 17:18:28 +02:00
|
|
|
}
|
|
|
|
|
2010-10-27 22:04:09 +02:00
|
|
|
// writing data
|
|
|
|
codedOutStream.writeTag(OsmandOdb.MapTree.LEAFS_FIELD_NUMBER, WireFormat.FieldType.MESSAGE.getWireType());
|
|
|
|
codedOutStream.writeRawVarint32(allSize);
|
|
|
|
|
|
|
|
codedOutStream.writeTag(OsmandOdb.MapData.COORDINATES_FIELD_NUMBER, WireFormat.FieldType.BYTES.getWireType());
|
|
|
|
codedOutStream.writeRawVarint32(sizeCoordinates);
|
|
|
|
for (int i = 0; i < nodes.length / 8; i++) {
|
|
|
|
int x = Algoritms.parseIntFromBytes(nodes, i * 8) - bounds.leftX;
|
|
|
|
int y = Algoritms.parseIntFromBytes(nodes, i * 8 + 4) - bounds.topY;
|
|
|
|
codedOutStream.writeInt32NoTag(x);
|
|
|
|
codedOutStream.writeInt32NoTag(y);
|
|
|
|
}
|
|
|
|
|
|
|
|
codedOutStream.writeTag(OsmandOdb.MapData.TYPES_FIELD_NUMBER, WireFormat.FieldType.BYTES.getWireType());
|
|
|
|
codedOutStream.writeRawVarint32(types.length);
|
|
|
|
codedOutStream.writeRawBytes(types);
|
|
|
|
|
|
|
|
|
|
|
|
codedOutStream.writeSInt64(OsmandOdb.MapData.ID_FIELD_NUMBER, id - stackBaseIds.peek());
|
|
|
|
|
|
|
|
if(name != null){
|
|
|
|
codedOutStream.writeUInt32(OsmandOdb.MapData.STRINGID_FIELD_NUMBER, nameId);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(restrictions.length > 0){
|
|
|
|
codedOutStream.writeTag(OsmandOdb.MapData.RESTRICTIONS_FIELD_NUMBER, WireFormat.FieldType.BYTES.getWireType());
|
|
|
|
codedOutStream.writeRawVarint32(restrictionsSize);
|
|
|
|
for (int i = 0; i < restrictions.length / 8; i++) {
|
|
|
|
long l = Algoritms.parseLongFromBytes(restrictions, i * 8) - stackBaseIds.peek();
|
|
|
|
codedOutStream.writeSInt64NoTag(l);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(highwayAttributes != 0){
|
|
|
|
codedOutStream.writeInt32(OsmandOdb.MapData.HIGHWAYMETA_FIELD_NUMBER, highwayAttributes);
|
|
|
|
}
|
2010-10-27 12:36:07 +02:00
|
|
|
}
|
2010-10-27 17:18:28 +02:00
|
|
|
|
2010-10-27 12:36:07 +02:00
|
|
|
|
|
|
|
public void close() throws IOException{
|
|
|
|
assert state.peek() == OSMAND_STRUCTURE_INIT;
|
|
|
|
codedOutStream.flush();
|
|
|
|
}
|
|
|
|
}
|