From 2be2ab111aecd9c99ed26fac16336b459bcb9f47 Mon Sep 17 00:00:00 2001 From: Hannes Janetzek Date: Sat, 25 May 2013 21:19:08 +0200 Subject: [PATCH] refactor map databases --- .../oscim/database/MapDatabaseFactory.java | 10 +- src/org/oscim/database/MapDatabases.java | 10 +- src/org/oscim/database/common/LwHttp.java | 102 +- .../database/common/ProtobufDecoder.java | 399 +++--- .../database/common/ProtobufMapDatabase.java | 136 ++ .../oscim/database/mapnik/MapDatabase.java | 117 +- .../oscim/database/mapnik/TileDecoder.java | 50 +- .../oscim/database/oscimap/MapDatabase.java | 1165 +---------------- .../oscim/database/oscimap/TileDecoder.java | 449 +++++++ src/org/oscim/database/oscimap2/LwHttp.java | 450 ------- .../oscim/database/oscimap2/MapDatabase.java | 822 ++++-------- .../oscim/database/oscimap4/MapDatabase.java | 123 +- .../oscim/database/oscimap4/TileDecoder.java | 27 +- .../layers/tile/vector/MapTileLayer.java | 9 +- 14 files changed, 1189 insertions(+), 2680 deletions(-) create mode 100644 src/org/oscim/database/common/ProtobufMapDatabase.java create mode 100644 src/org/oscim/database/oscimap/TileDecoder.java delete mode 100644 src/org/oscim/database/oscimap2/LwHttp.java diff --git a/src/org/oscim/database/MapDatabaseFactory.java b/src/org/oscim/database/MapDatabaseFactory.java index 83ffdabb..9f65515e 100644 --- a/src/org/oscim/database/MapDatabaseFactory.java +++ b/src/org/oscim/database/MapDatabaseFactory.java @@ -27,7 +27,7 @@ public final class MapDatabaseFactory { String mapDatabaseName = attributeSet.getAttributeValue(null, MAP_DATABASE_ATTRIBUTE_NAME); if (mapDatabaseName == null) { - return MapDatabases.OSCIMAP_READER; + return MapDatabases.OPENSCIENCEMAP2; } return MapDatabases.valueOf(mapDatabaseName); @@ -40,13 +40,13 @@ public final class MapDatabaseFactory { */ public static IMapDatabase createMapDatabase(MapDatabases mapDatabase) { switch (mapDatabase) { - case MAPSFORGE_FILE: + case MAPSFORGE: return new org.oscim.database.mapfile.MapDatabase(); - case TEST_READER: + case TEST: return new org.oscim.database.test.MapDatabase(); - case PBMAP_READER: + case OPENSCIENCEMAP1: return new org.oscim.database.oscimap.MapDatabase(); - case OSCIMAP_READER: + case OPENSCIENCEMAP2: return new org.oscim.database.oscimap2.MapDatabase(); case OPENSCIENCEMAP4: return new org.oscim.database.oscimap4.MapDatabase(); diff --git a/src/org/oscim/database/MapDatabases.java b/src/org/oscim/database/MapDatabases.java index 1be4bbc6..dacf9b0e 100644 --- a/src/org/oscim/database/MapDatabases.java +++ b/src/org/oscim/database/MapDatabases.java @@ -21,26 +21,26 @@ public enum MapDatabases { /** * ... */ - MAPSFORGE_FILE, + TEST, /** * ... */ - TEST_READER, + MAPSFORGE, /** * ... */ - POSTGIS_READER, + POSTGIS, /** * ... */ - PBMAP_READER, + OPENSCIENCEMAP1, /** * ... */ - OSCIMAP_READER, + OPENSCIENCEMAP2, /** * ... diff --git a/src/org/oscim/database/common/LwHttp.java b/src/org/oscim/database/common/LwHttp.java index 87c04c40..f6471841 100644 --- a/src/org/oscim/database/common/LwHttp.java +++ b/src/org/oscim/database/common/LwHttp.java @@ -30,11 +30,13 @@ import org.oscim.core.Tile; import android.os.SystemClock; import android.util.Log; -public class LwHttp extends InputStream{ +public class LwHttp { private static final String TAG = LwHttp.class.getName(); //private static final boolean DEBUG = false; - private final static byte[] RESPONSE_HTTP_OK = "200 OK".getBytes(); + private final static byte[] HEADER_HTTP_OK = "200 OK".getBytes(); + private final static byte[] HEADER_CONTENT_TYPE = "Content-Type".getBytes(); + private final static byte[] HEADER_CONTENT_LENGTH = "Content-Length".getBytes(); private final static int RESPONSE_EXPECTED_LIVES = 100; private final static int RESPONSE_TIMEOUT = 10000; @@ -55,17 +57,43 @@ public class LwHttp extends InputStream{ private byte[] REQUEST_GET_END; private byte[] mRequestBuffer; - private boolean mInflateContent; + private final boolean mInflateContent; + private final byte[] mContentType; + private final String mExtension; + + private int mContentLength = -1; + + public LwHttp(String contentType, String extension, boolean deflate) { + mExtension = extension; + mContentType = contentType.getBytes(); + mInflateContent = deflate; + + } + + static class Buffer extends BufferedInputStream { + public Buffer(InputStream is) { + super(is, 4096); + } + + @Override + public synchronized int read() throws IOException { + return super.read(); + } + + @Override + public synchronized int read(byte[] buffer, int offset, int byteCount) throws IOException { + return super.read(buffer, offset, byteCount); + } + } + + public boolean setServer(String urlString) { - public boolean setServer(String urlString, String extension, boolean zlibDeflate) { URL url; try { url = new URL(urlString); } catch (MalformedURLException e) { - e.printStackTrace(); return false; - //return new OpenResult("invalid url: " + options.get("url")); } int port = url.getPort(); @@ -74,13 +102,14 @@ public class LwHttp extends InputStream{ String host = url.getHost(); String path = url.getPath(); - Log.d(TAG, "open oscim database: " + host + " " + port + " " + path); + Log.d(TAG, "open database: " + host + " " + port + " " + path); REQUEST_GET_START = ("GET " + path).getBytes(); - REQUEST_GET_END = (extension + " HTTP/1.1\n" + - "Host: " + host + "\n" + - "Connection: Keep-Alive\n\n").getBytes(); - mInflateContent = zlibDeflate; + + REQUEST_GET_END = ("." + mExtension + " HTTP/1.1" + + "\nHost: " + host + + "\nConnection: Keep-Alive" + + "\n\n").getBytes(); mHost = host; mPort = port; @@ -91,7 +120,6 @@ public class LwHttp extends InputStream{ return true; } - @Override public void close() { if (mSocket != null) { try { @@ -117,6 +145,8 @@ public class LwHttp extends InputStream{ int end = 0; int len = 0; + mContentLength = -1; + // header cannot be larger than BUFFER_SIZE for this to work for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) { read += len; @@ -129,7 +159,7 @@ public class LwHttp extends InputStream{ if (first) { // check only for OK first = false; - if (!compareBytes(buf, pos + 9, end, RESPONSE_HTTP_OK, 6)) { + if (!check(HEADER_HTTP_OK, 6, buf, pos + 9, end)) { String line = new String(buf, pos, end - pos - 1); Log.d(TAG, ">" + line + "< "); return null; @@ -138,10 +168,16 @@ public class LwHttp extends InputStream{ // check empty line (header end) end += 1; break; + } else if (check(HEADER_CONTENT_TYPE, 12, buf, pos, end)) { + if (!check(mContentType, mContentType.length, buf, pos + 14, end)) + return null; + } else if (check(HEADER_CONTENT_LENGTH, 14, buf, pos, end)) { + mContentLength = parseInt(pos + 16, end-1, buf); + } //String line = new String(buf, pos, end - pos - 1); - //Log.d(TAG, ">" + line + "< "); + //Log.d(TAG, ">" + line + "< " + mContentLength); pos += (end - pos) + 1; end = pos; @@ -181,11 +217,11 @@ public class LwHttp extends InputStream{ // Log.d(TAG, "create connection"); } else { int avail = mResponseStream.available(); - if (avail > 0){ - Log.d(TAG, "Consume left-over bytes: " + avail); + if (avail > 0) { + Log.d(TAG, "Consume left-over bytes: " + avail); - while ((avail = mResponseStream.available()) > 0) - mResponseStream.read(buffer); + while ((avail = mResponseStream.available()) > 0) + mResponseStream.read(buffer); Log.d(TAG, "Consumed bytes"); } } @@ -234,12 +270,12 @@ public class LwHttp extends InputStream{ mSocket.setTcpNoDelay(true); mCommandStream = mSocket.getOutputStream(); - mResponseStream = new BufferedInputStream(mSocket.getInputStream(), 4096); + mResponseStream = new BufferedInputStream(mSocket.getInputStream()); return true; } - // write (positive) integer as char sequence to buffer + // write (positive) integer to byte array protected static int writeInt(int val, int pos, byte[] buf) { if (val == 0) { buf[pos] = '0'; @@ -259,9 +295,17 @@ public class LwHttp extends InputStream{ return pos + i; } + // parse (positive) integer from byte array + protected static int parseInt(int pos, int end, byte[] buf) { + int val = 0; + for (; pos < end; pos++) + val = val * 10 + (buf[pos]) - '0'; - private static boolean compareBytes(byte[] buffer, int position, int available, - byte[] string, int length) { + return val; + } + + private static boolean check(byte[] string, int length, byte[] buffer, + int position, int available) { if (available - position < length) return false; @@ -273,16 +317,13 @@ public class LwHttp extends InputStream{ return true; } - static int decodeInt(byte[] buffer, int offset) { - return buffer[offset] << 24 | (buffer[offset + 1] & 0xff) << 16 - | (buffer[offset + 2] & 0xff) << 8 - | (buffer[offset + 3] & 0xff); - } - public void requestCompleted() { mLastRequest = SystemClock.elapsedRealtime(); } + public int getContentLength(){ + return mContentLength; + } /** * Write custom tile url * @@ -295,10 +336,5 @@ public class LwHttp extends InputStream{ return 0; } - @Override - public int read() throws IOException { - return mResponseStream.read(); - } - } diff --git a/src/org/oscim/database/common/ProtobufDecoder.java b/src/org/oscim/database/common/ProtobufDecoder.java index b419fe9a..a71e5d7f 100644 --- a/src/org/oscim/database/common/ProtobufDecoder.java +++ b/src/org/oscim/database/common/ProtobufDecoder.java @@ -17,15 +17,33 @@ package org.oscim.database.common; import java.io.IOException; import java.io.InputStream; +import org.oscim.core.Tile; +import org.oscim.database.IMapDataSink; import org.oscim.utils.UTF8Decoder; import android.util.Log; -public class ProtobufDecoder { +public abstract class ProtobufDecoder { private final static String TAG = ProtobufDecoder.class.getName(); - private final static int VARINT_LIMIT = 6; - private final static int VARINT_MAX = 10; + protected static final boolean debug = false; + + static class ProtobufException extends IOException { + private static final long serialVersionUID = 1L; + + public ProtobufException(String detailMessage) { + super(detailMessage); + } + } + + final static ProtobufException TRUNCATED_MSG = new ProtobufException("truncated msg"); + protected final static ProtobufException INVALID_VARINT = new ProtobufException("invalid varint"); + protected final static ProtobufException INVALID_PACKED_SIZE = new ProtobufException( + "invalid message size"); + + protected void error(String msg) throws IOException { + throw new ProtobufException(msg); + } private final static int BUFFER_SIZE = 1 << 15; // 32kb protected byte[] buffer = new byte[BUFFER_SIZE]; @@ -34,7 +52,7 @@ public class ProtobufDecoder { protected int bufferPos; // bytes available in buffer - int bufferFill; + protected int bufferFill; // offset of buffer in message private int mBufferOffset; @@ -53,25 +71,8 @@ public class ProtobufDecoder { mStringDecoder = new UTF8Decoder(); } - protected static int readUnsignedInt(InputStream is, byte[] buf) throws IOException { - // check 4 bytes available.. - int read = 0; - int len = 0; - - while (read < 4 && (len = is.read(buf, read, 4 - read)) >= 0) - read += len; - - if (read < 4) - return read < 0 ? (read * 10) : read; - - return decodeInt(buf, 0); - } - - static int decodeInt(byte[] buffer, int offset) { - return buffer[offset] << 24 | (buffer[offset + 1] & 0xff) << 16 - | (buffer[offset + 2] & 0xff) << 8 - | (buffer[offset + 3] & 0xff); - } + public abstract boolean decode(Tile tile, IMapDataSink sink, + InputStream is, int contentLength) throws IOException; public void setInputStream(InputStream is, int contentLength) { mInputStream = is; @@ -84,16 +85,105 @@ public class ProtobufDecoder { mMsgEnd = contentLength; } -// public void skipAvailable() throws IOException { -// int bytes = decodeVarint32(); -// bufferPos += bytes; -// } + protected int decodeVarint32() throws IOException { - protected int decodeInterleavedPoints(float[] coords, int numPoints, float scale) + int bytesLeft = 0; + int val = 0; + + for (int shift = 0; shift < 32; shift += 7) { + if (bytesLeft == 0) + bytesLeft = fillBuffer(1); + + byte b = buffer[bufferPos++]; + val |= (b & 0x7f) << shift; + + if (b >= 0) + return val; + + bytesLeft--; + } + + throw INVALID_VARINT; + } + + protected long decodeVarint64() throws IOException { + + int bytesLeft = 0; + long val = 0; + + for (int shift = 0; shift < 64; shift += 7) { + if (bytesLeft == 0) + bytesLeft = fillBuffer(1); + + byte b = buffer[bufferPos++]; + val |= (long) (b & 0x7f) << shift; + + if (b >= 0) + return val; + + bytesLeft--; + } + + throw INVALID_VARINT; + } + + protected String decodeString() throws IOException { + String result; + + final int size = decodeVarint32(); + fillBuffer(size); + + if (mStringDecoder == null) + result = new String(buffer, bufferPos, size, "UTF-8"); + else + result = mStringDecoder.decode(buffer, bufferPos, size); + + bufferPos += size; + + return result; + + } + + protected float decodeFloat() throws IOException { + if (bufferPos + 4 > bufferFill) + fillBuffer(4); + + int val = (buffer[bufferPos++] & 0xFF + | (buffer[bufferPos++] & 0xFF) << 8 + | (buffer[bufferPos++] & 0xFF) << 16 + | (buffer[bufferPos++] & 0xFF) << 24); + + return Float.intBitsToFloat(val); + } + + protected double decodeDouble() throws IOException { + if (bufferPos + 8 > bufferFill) + fillBuffer(8); + + long val = (buffer[bufferPos++] & 0xFF + | (buffer[bufferPos++] & 0xFF) << 8 + | (buffer[bufferPos++] & 0xFF) << 16 + | (buffer[bufferPos++] & 0xFF) << 24 + | (buffer[bufferPos++] & 0xFF) << 32 + | (buffer[bufferPos++] & 0xFF) << 40 + | (buffer[bufferPos++] & 0xFF) << 48 + | (buffer[bufferPos++] & 0xFF) << 56); + + return Double.longBitsToDouble(val); + } + + protected boolean decodeBool() throws IOException { + if (bufferPos + 1 > bufferFill) + fillBuffer(1); + + return buffer[bufferPos++] != 0; + } + + protected int decodeInterleavedPoints(float[] coords, float scale) throws IOException { - int bytes = decodeVarint32(); - readBuffer(bytes); + int bytes = decodeVarint32(); + fillBuffer(bytes); int cnt = 0; int lastX = 0; @@ -131,10 +221,8 @@ public class ProtobufDecoder { | (buf[pos++] & 0x7f) << 21 | (buf[pos]) << 28; - int max = pos + VARINT_LIMIT; - while (buf[pos++] < 0) - if (pos == max) - throw new IOException("malformed VarInt32"); + if (buf[pos++] < 0) + throw INVALID_VARINT; } // zigzag decoding @@ -150,29 +238,34 @@ public class ProtobufDecoder { even = true; } } + if (pos != bufferPos + bytes) - throw new IOException("invalid array " + numPoints); + throw INVALID_PACKED_SIZE; bufferPos = pos; - return cnt; + // return number of points read + return (cnt >> 1); + } + + protected static int deZigZag(int val) { + return ((val >>> 1) ^ -(val & 1)); } public void decodeVarintArray(int num, short[] array) throws IOException { int bytes = decodeVarint32(); - - readBuffer(bytes); - - int cnt = 0; + fillBuffer(bytes); byte[] buf = buffer; int pos = bufferPos; int end = pos + bytes; int val; + int cnt = 0; + while (pos < end) { if (cnt == num) - throw new IOException("invalid array size " + num); + throw new ProtobufException("invalid array size " + num); if (buf[pos] >= 0) { val = buf[pos++]; @@ -188,20 +281,20 @@ public class ProtobufDecoder { | (buf[pos++] & 0x7f) << 7 | (buf[pos++] & 0x7f) << 14 | (buf[pos++]) << 21; - } else if (buf[pos + 4] >= 0) { + } else { val = (buf[pos++] & 0x7f) | (buf[pos++] & 0x7f) << 7 | (buf[pos++] & 0x7f) << 14 | (buf[pos++] & 0x7f) << 21 - | (buf[pos++]) << 28; - } else - throw new IOException("malformed VarInt32"); - + | (buf[pos]) << 28; + if (buf[pos++] < 0) + throw INVALID_VARINT; + } array[cnt++] = (short) val; } if (pos != bufferPos + bytes) - throw new IOException("invalid array " + num); + throw INVALID_PACKED_SIZE; bufferPos = pos; } @@ -220,7 +313,7 @@ public class ProtobufDecoder { array = new short[32]; } - readBuffer(bytes); + fillBuffer(bytes); int cnt = 0; byte[] buf = buffer; @@ -250,13 +343,8 @@ public class ProtobufDecoder { | (buf[pos++] & 0x7f) << 21 | (buf[pos]) << 28; - int max = pos + VARINT_LIMIT; - while (pos < max) - if (buf[pos++] >= 0) - break; - - if (pos > max) - throw new IOException("malformed VarInt32"); + if (buf[pos++] < 0) + throw INVALID_VARINT; } if (arrayLength <= cnt) { @@ -269,6 +357,9 @@ public class ProtobufDecoder { array[cnt++] = (short) val; } + if (pos != bufferPos + bytes) + throw INVALID_PACKED_SIZE; + bufferPos = pos; if (arrayLength > cnt) @@ -277,16 +368,8 @@ public class ProtobufDecoder { return array; } - protected int decodeVarint32() throws IOException { - if (bufferPos + VARINT_MAX > bufferFill) - readBuffer(4096); - - return decodeVarint32Filled(); - } - + // for use int packed varint decoders protected int decodeVarint32Filled() throws IOException { - if (bufferPos + VARINT_MAX > bufferFill) - readBuffer(4096); byte[] buf = buffer; int pos = bufferPos; @@ -295,7 +378,6 @@ public class ProtobufDecoder { if (buf[pos] >= 0) { val = buf[pos++]; } else { - if (buf[pos + 1] >= 0) { val = (buf[pos++] & 0x7f) | (buf[pos++]) << 7; @@ -317,14 +399,8 @@ public class ProtobufDecoder { | (buf[pos++] & 0x7f) << 21 | (buf[pos]) << 28; - // 'Discard upper 32 bits' - int max = pos + VARINT_LIMIT; - while (pos < max) - if (buf[pos++] >= 0) - break; - - if (pos == max) - throw new IOException("malformed VarInt32"); + if (buf[pos++] < 0) + throw INVALID_VARINT; } } @@ -332,173 +408,86 @@ public class ProtobufDecoder { return val; } - // FIXME this also accept uin64 atm. -// protected int decodeVarint32Filled() throws IOException { -// -// if (buffer[bufferPos] >= 0) -// return buffer[bufferPos++]; -// -// byte[] buf = buffer; -// int pos = bufferPos; -// int val = 0; -// -// if (buf[pos + 1] >= 0) { -// val = (buf[pos++] & 0x7f) -// | (buf[pos++]) << 7; -// -// } else if (buf[pos + 2] >= 0) { -// val = (buf[pos++] & 0x7f) -// | (buf[pos++] & 0x7f) << 7 -// | (buf[pos++]) << 14; -// -// } else if (buf[pos + 3] >= 0) { -// val = (buf[pos++] & 0x7f) -// | (buf[pos++] & 0x7f) << 7 -// | (buf[pos++] & 0x7f) << 14 -// | (buf[pos++]) << 21; -// } else { -// val = (buf[pos++] & 0x7f) -// | (buf[pos++] & 0x7f) << 7 -// | (buf[pos++] & 0x7f) << 14 -// | (buf[pos++] & 0x7f) << 21 -// | (buf[pos]) << 28; -// -// -// // 'Discard upper 32 bits' -// int max = pos + VARINT_LIMIT; -// while (pos < max) -// if (buf[pos++] >= 0) -// break; -// if (pos == max) -// throw new IOException("malformed VarInt32"); -// } -// bufferPos = pos; -// -// return val; -// } - - public String decodeString() throws IOException { - final int size = decodeVarint32(); - readBuffer(size); - - String result; - - if (mStringDecoder == null) - result = new String(buffer, bufferPos, size, "UTF-8"); - else - result = mStringDecoder.decode(buffer, bufferPos, size); - - bufferPos += size; - - return result; - - } - - public float decodeFloat() throws IOException { - if (bufferPos + 4 > bufferFill) - readBuffer(4096); - - byte[] buf = buffer; - int pos = bufferPos; - - int val = (buf[pos++] & 0xFF - | (buf[pos++] & 0xFF) << 8 - | (buf[pos++] & 0xFF) << 16 - | (buf[pos++] & 0xFF) << 24); - - bufferPos += 4; - return Float.intBitsToFloat(val); - } - - public double decodeDouble() throws IOException { - if (bufferPos + 8 > bufferFill) - readBuffer(4096); - - byte[] buf = buffer; - int pos = bufferPos; - - long val = (buf[pos++] & 0xFF - | (buf[pos++] & 0xFF) << 8 - | (buf[pos++] & 0xFF) << 16 - | (buf[pos++] & 0xFF) << 24 - | (buf[pos++] & 0xFF) << 32 - | (buf[pos++] & 0xFF) << 40 - | (buf[pos++] & 0xFF) << 48 - | (buf[pos++] & 0xFF) << 56); - - bufferPos += 8; - return Double.longBitsToDouble(val); - } - - public boolean decodeBool() throws IOException { - if (bufferPos + 1 > bufferFill) - readBuffer(4096); - - return buffer[bufferPos++] != 0; - } public boolean hasData() throws IOException { if (mBufferOffset + bufferPos >= mMsgEnd) return false; - return readBuffer(1); + return fillBuffer(1) > 0; } public int position() { return mBufferOffset + bufferPos; } - public boolean readBuffer(int size) throws IOException { + public int fillBuffer(int size) throws IOException { + int bytesLeft = bufferFill - bufferPos; + // check if buffer already contains the request bytes - if (bufferPos + size < bufferFill) - return true; + if (bytesLeft >= size) + return bytesLeft; // check if inputstream is read to the end if (mMsgPos >= mMsgEnd) - return false; + return bytesLeft; int maxSize = buffer.length; if (size > maxSize) { - Log.d(TAG, "increase read buffer to " + size + " bytes"); + + if (debug) + Log.d(TAG, "increase read buffer to " + size + " bytes"); + maxSize = size; - bufferFill -= bufferPos; byte[] tmp = buffer; buffer = new byte[maxSize]; - System.arraycopy(tmp, bufferPos, buffer, 0, bufferFill); + System.arraycopy(tmp, bufferPos, buffer, 0, bytesLeft); mBufferOffset += bufferPos; bufferPos = 0; - } + bufferFill = bytesLeft; - if (bufferFill == bufferPos) { + } else if (bytesLeft == 0) { + // just advance buffer offset and reset buffer mBufferOffset += bufferPos; bufferPos = 0; bufferFill = 0; + } else if (bufferPos + size > maxSize) { // copy bytes left to the beginning of buffer - bufferFill -= bufferPos; - System.arraycopy(buffer, bufferPos, buffer, 0, bufferFill); + if (debug) + Log.d(TAG, "shift " + bufferFill + " " + bufferPos + " " + size); + + System.arraycopy(buffer, bufferPos, buffer, 0, bytesLeft); + mBufferOffset += bufferPos; bufferPos = 0; + bufferFill = bytesLeft; } - int max = maxSize - bufferFill; + while ((bufferFill - bufferPos) < size) { - while ((bufferFill - bufferPos) < size && max > 0) { - - max = maxSize - bufferFill; + int max = maxSize - bufferFill; if (max > mMsgEnd - mMsgPos) max = mMsgEnd - mMsgPos; + if (max <= 0) { + // should not be possible + throw new IOException("burp"); + } + // read until requested size is available in buffer int len = mInputStream.read(buffer, bufferFill, max); if (len < 0) { + mMsgEnd = mMsgPos; + if (debug) + Log.d(TAG, " finished reading " + mMsgPos); + // finished reading, mark end buffer[bufferFill] = 0; - return false; + return bufferFill - bufferPos; } mMsgPos += len; @@ -508,6 +497,26 @@ public class ProtobufDecoder { break; } - return true; + return bufferFill - bufferPos; + } + + protected static int readUnsignedInt(InputStream is, byte[] buf) throws IOException { + // check 4 bytes available.. + int read = 0; + int len = 0; + + while (read < 4 && (len = is.read(buf, read, 4 - read)) >= 0) + read += len; + + if (read < 4) + return read < 0 ? (read * 10) : read; + + return decodeInt(buf, 0); + } + + static int decodeInt(byte[] buffer, int offset) { + return buffer[offset] << 24 | (buffer[offset + 1] & 0xff) << 16 + | (buffer[offset + 2] & 0xff) << 8 + | (buffer[offset + 3] & 0xff); } } diff --git a/src/org/oscim/database/common/ProtobufMapDatabase.java b/src/org/oscim/database/common/ProtobufMapDatabase.java new file mode 100644 index 00000000..5d756bf9 --- /dev/null +++ b/src/org/oscim/database/common/ProtobufMapDatabase.java @@ -0,0 +1,136 @@ +/* + * Copyright 2012 Hannes Janetzek + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.database.common; + +import java.io.InputStream; +import java.net.SocketException; +import java.net.SocketTimeoutException; +import java.net.UnknownHostException; + +import org.oscim.core.BoundingBox; +import org.oscim.core.GeoPoint; +import org.oscim.database.IMapDataSink; +import org.oscim.database.IMapDatabase; +import org.oscim.database.MapInfo; +import org.oscim.database.MapOptions; +import org.oscim.layers.tile.MapTile; + +import android.util.Log; + +/** + * + * + */ +public abstract class ProtobufMapDatabase implements IMapDatabase { + private static final String TAG = ProtobufMapDatabase.class.getName(); + + protected LwHttp mConn; + protected final ProtobufDecoder mTileDecoder; + + // 'open' state + private boolean mOpen = false; + + public ProtobufMapDatabase(ProtobufDecoder tileDecoder) { + mTileDecoder = tileDecoder; + } + + private static final MapInfo mMapInfo = + new MapInfo(new BoundingBox(-180, -90, 180, 90), + new Byte((byte) 4), new GeoPoint(53.11, 8.85), + null, 0, 0, 0, "de", "comment", "author", null); + + @Override + public QueryResult executeQuery(MapTile tile, IMapDataSink sink) { + QueryResult result = QueryResult.SUCCESS; + + try { + InputStream is; + if (!mConn.sendRequest(tile)) { + Log.d(TAG, tile + " Request Failed"); + result = QueryResult.FAILED; + } else if ((is = mConn.readHeader()) != null) { + boolean win = mTileDecoder.decode(tile, sink, is, mConn.getContentLength()); + if (!win) + Log.d(TAG, tile + " failed"); + } else { + Log.d(TAG, tile + " Network Error"); + result = QueryResult.FAILED; + } + } catch (SocketException e) { + Log.d(TAG, tile + " Socket exception: " + e.getMessage()); + result = QueryResult.FAILED; + } catch (SocketTimeoutException e) { + Log.d(TAG, tile + " Socket Timeout"); + result = QueryResult.FAILED; + } catch (UnknownHostException e) { + Log.d(TAG, tile + " No Network"); + result = QueryResult.FAILED; + } catch (Exception e) { + e.printStackTrace(); + result = QueryResult.FAILED; + } + + mConn.requestCompleted(); + + if (result != QueryResult.SUCCESS) + mConn.close(); + + return result; + } + + @Override + public String getMapProjection() { + return null; + } + + @Override + public MapInfo getMapInfo() { + return mMapInfo; + } + + @Override + public boolean isOpen() { + return mOpen; + } + + @Override + public OpenResult open(MapOptions options) { + + if (mOpen) + return OpenResult.SUCCESS; + + if (options == null || !options.containsKey("url")) + return new OpenResult("No URL in MapOptions"); + + + if (!mConn.setServer(options.get("url"))) { + return new OpenResult("invalid url: " + options.get("url")); + } + + mOpen = true; + + return OpenResult.SUCCESS; + } + + @Override + public void close() { + mOpen = false; + mConn.close(); + } + + @Override + public void cancel() { + } +} diff --git a/src/org/oscim/database/mapnik/MapDatabase.java b/src/org/oscim/database/mapnik/MapDatabase.java index a552e5d1..6f173ae0 100644 --- a/src/org/oscim/database/mapnik/MapDatabase.java +++ b/src/org/oscim/database/mapnik/MapDatabase.java @@ -14,95 +14,17 @@ */ package org.oscim.database.mapnik; -import java.io.InputStream; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; - -import org.oscim.core.BoundingBox; -import org.oscim.core.GeoPoint; import org.oscim.core.Tile; -import org.oscim.database.IMapDataSink; -import org.oscim.database.IMapDatabase; -import org.oscim.database.MapInfo; -import org.oscim.database.MapOptions; import org.oscim.database.common.LwHttp; -import org.oscim.layers.tile.MapTile; +import org.oscim.database.common.ProtobufMapDatabase; -import android.util.Log; +public class MapDatabase extends ProtobufMapDatabase { + //private static final String TAG = MapDatabase.class.getName(); -public class MapDatabase implements IMapDatabase { - private static final String TAG = MapDatabase.class.getName(); - - private static final MapInfo mMapInfo = - new MapInfo(new BoundingBox(-180, -90, 180, 90), - new Byte((byte) 4), new GeoPoint(53.11, 8.85), - null, 0, 0, 0, "de", "comment", "author", - new int[] { 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 } - ); - - // 'open' state - private boolean mOpen = false; - - private LwHttp conn; - private TileDecoder mTileDecoder; - - @Override - public QueryResult executeQuery(MapTile tile, IMapDataSink mapDataSink) { - QueryResult result = QueryResult.SUCCESS; - - try { - InputStream is; - if (conn.sendRequest(tile) && (is = conn.readHeader()) != null) { - mTileDecoder.decode(is, tile, mapDataSink); - } else { - Log.d(TAG, tile + " Network Error"); - result = QueryResult.FAILED; - } - } catch (SocketException ex) { - Log.d(TAG, tile + " Socket exception: " + ex.getMessage()); - result = QueryResult.FAILED; - } catch (SocketTimeoutException ex) { - Log.d(TAG, tile + " Socket Timeout exception: " + ex.getMessage()); - result = QueryResult.FAILED; - } catch (UnknownHostException ex) { - Log.d(TAG, tile + " no network"); - result = QueryResult.FAILED; - } catch (Exception ex) { - ex.printStackTrace(); - result = QueryResult.FAILED; - } - - conn.requestCompleted(); - - if (result != QueryResult.SUCCESS) { - conn.close(); - } - - return result; - } - - @Override - public MapInfo getMapInfo() { - return mMapInfo; - } - - @Override - public boolean isOpen() { - return mOpen; - } - - @Override - public OpenResult open(MapOptions options) { - String extension = ".vector.pbf"; - if (mOpen) - return OpenResult.SUCCESS; - - if (options == null || !options.containsKey("url")) - return new OpenResult("options missing"); - - conn = new LwHttp() { + public MapDatabase() { + super(new TileDecoder()); + mConn = new LwHttp("image/png","vector.pbf", true) { @Override protected int formatTilePath(Tile tile, byte[] path, int pos) { // url formatter for mapbox streets @@ -122,32 +44,5 @@ public class MapDatabase implements IMapDatabase { return pos; } }; - - if (!conn.setServer(options.get("url"), extension, true)) { - return new OpenResult("invalid url: " + options.get("url")); - } - - mTileDecoder = new TileDecoder(); - - mOpen = true; - return OpenResult.SUCCESS; - } - - @Override - public void close() { - mOpen = false; - - mTileDecoder = null; - conn.close(); - conn = null; - } - - @Override - public String getMapProjection() { - return null; - } - - @Override - public void cancel() { } } diff --git a/src/org/oscim/database/mapnik/TileDecoder.java b/src/org/oscim/database/mapnik/TileDecoder.java index 410f22d7..32f5f98e 100644 --- a/src/org/oscim/database/mapnik/TileDecoder.java +++ b/src/org/oscim/database/mapnik/TileDecoder.java @@ -70,7 +70,12 @@ public class TileDecoder extends ProtobufDecoder { private final static float REF_TILE_SIZE = 4096.0f; private float mScale; - boolean decode(InputStream is, Tile tile, IMapDataSink mapDataCallback) throws IOException { + @Override + public boolean decode(Tile tile, IMapDataSink mapDataCallback, InputStream is, int contentLength) + throws IOException { + if (debug) + Log.d(TAG, tile + " decode"); + setInputStream(is, Integer.MAX_VALUE); mTile = tile; mMapDataCallback = mapDataCallback; @@ -88,10 +93,15 @@ public class TileDecoder extends ProtobufDecoder { break; default: - Log.d(TAG, mTile + " invalid type for tile: " + tag); + error(mTile + " invalid type for tile: " + tag); return false; } } + + if (hasData()){ + error(tile + " invalid tile"); + return false; + } return true; } @@ -147,7 +157,7 @@ public class TileDecoder extends ProtobufDecoder { break; default: - Log.d(TAG, mTile + " invalid type for layer: " + tag); + error(mTile + " invalid type for layer: " + tag); break; } @@ -357,7 +367,7 @@ public class TileDecoder extends ProtobufDecoder { break; default: - Log.d(TAG, mTile + " invalid type for feature: " + tag); + error(mTile + " invalid type for feature: " + tag); break; } } @@ -371,7 +381,7 @@ public class TileDecoder extends ProtobufDecoder { private int decodeCoordinates(int type, Feature feature) throws IOException { int bytes = decodeVarint32(); - readBuffer(bytes); + fillBuffer(bytes); if (feature == null) { bufferPos += bytes; @@ -405,7 +415,7 @@ public class TileDecoder extends ProtobufDecoder { int prevY = 0; int cmd = 0; - int num = 0; + int num = 0, cnt = 0; boolean first = true; boolean lastClip = false; @@ -413,7 +423,7 @@ public class TileDecoder extends ProtobufDecoder { // test bbox for outer.. boolean isOuter = true; boolean simplify = mTile.zoomLevel < 14; - int pixel = simplify ? 7 : 1; + int pixel = simplify ? 7 : 3; int xmin = Integer.MAX_VALUE, xmax = Integer.MIN_VALUE; int ymin = Integer.MAX_VALUE, ymax = Integer.MIN_VALUE; @@ -422,7 +432,10 @@ public class TileDecoder extends ProtobufDecoder { val = decodeVarint32Filled(); if (num == 0) { + // number of points num = val >>> 3; + cnt = 0; + // path command cmd = val & 0x07; if (isLine && lastClip) { @@ -466,17 +479,31 @@ public class TileDecoder extends ProtobufDecoder { int dx = (curX - prevX); int dy = (curY - prevY); + if (isPoly && num == 0 && cnt > 0){ + prevX = curX; + prevY = curY; + + // only add last point if it is di + int ppos = cnt * 2; + if (elem.points[elem.pointPos - ppos] != curX + || elem.points[elem.pointPos - ppos + 1] != curY) + elem.addPoint(curX / mScale, curY / mScale); + + lastClip = false; + continue; + } + if ((isPoint || cmd == MOVE_TO) || (dx > pixel || dx < -pixel) || (dy > pixel || dy < -pixel) - // dont clip at tile boundaries + // hack to not clip at tile boundaries || (curX <= 0 || curX >= 4095) || (curY <= 0 || curY >= 4095)) { prevX = curX; prevY = curY; elem.addPoint(curX / mScale, curY / mScale); - lastClip = false; + cnt++; if (simplify && isOuter) { if (curX < xmin) @@ -490,6 +517,7 @@ public class TileDecoder extends ProtobufDecoder { ymax = curY; } + lastClip = false; continue; } lastClip = true; @@ -543,11 +571,11 @@ public class TileDecoder extends ProtobufDecoder { break; case TAG_VALUE_SINT: - value = String.valueOf(decodeVarint32()); + value = String.valueOf(deZigZag(decodeVarint32())); break; case TAG_VALUE_LONG: - value = String.valueOf(decodeVarint32()); + value = String.valueOf(decodeVarint64()); break; case TAG_VALUE_FLOAT: diff --git a/src/org/oscim/database/oscimap/MapDatabase.java b/src/org/oscim/database/oscimap/MapDatabase.java index 5b6f9d0e..295216e2 100644 --- a/src/org/oscim/database/oscimap/MapDatabase.java +++ b/src/org/oscim/database/oscimap/MapDatabase.java @@ -14,1169 +14,16 @@ */ package org.oscim.database.oscimap; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.Socket; -import java.net.SocketAddress; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.URL; -import java.net.UnknownHostException; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.oscim.core.BoundingBox; -import org.oscim.core.GeoPoint; -import org.oscim.core.GeometryBuffer.GeometryType; -import org.oscim.core.MapElement; -import org.oscim.core.Tag; -import org.oscim.core.Tile; -import org.oscim.database.IMapDataSink; -import org.oscim.database.IMapDatabase; -import org.oscim.database.MapInfo; -import org.oscim.database.MapOptions; -import org.oscim.layers.tile.MapTile; - -import android.os.Environment; -import android.os.SystemClock; -import android.util.Log; +import org.oscim.database.common.LwHttp; +import org.oscim.database.common.ProtobufMapDatabase; /** * Deprecated * */ -public class MapDatabase implements IMapDatabase { - private static final String TAG = "MapDatabase"; - - private static final MapInfo mMapInfo = - new MapInfo(new BoundingBox(-180, -90, 180, 90), - new Byte((byte) 4), new GeoPoint(53.11, 8.85), - null, 0, 0, 0, "de", "comment", "author", null); - - private boolean mOpenFile = false; - - private static final boolean USE_CACHE = false; - - // private static final boolean USE_APACHE_HTTP = false; - // private static final boolean USE_LW_HTTP = true; - - private static final String CACHE_DIRECTORY = "/Android/data/org.oscim.app/cache/"; - private static final String CACHE_FILE = "%d-%d-%d.tile"; - - // private static final String SERVER_ADDR = "city.informatik.uni-bremen.de"; - // private static final String URL = - // "http://city.informatik.uni-bremen.de:8020/test/%d/%d/%d.osmtile"; - //private static final String URL = "http://city.informatik.uni-bremen.de/osmstache/test/%d/%d/%d.osmtile"; - //private static final String URL = "http://city.informatik.uni-bremen.de/osmstache/gis-live/%d/%d/%d.osmtile"; - - // private static final String URL = - // "http://city.informatik.uni-bremen.de/tiles/tiles.py///test/%d/%d/%d.osmtile"; - // private static final String URL = - // "http://city.informatik.uni-bremen.de/osmstache/gis2/%d/%d/%d.osmtile"; - - private final static float REF_TILE_SIZE = 4096.0f; - - private int MAX_TILE_TAGS = 100; - private Tag[] curTags = new Tag[MAX_TILE_TAGS]; - private int mCurTagCnt; - - private IMapDataSink mMapGenerator; - private float mScaleFactor; - private MapTile mTile; - private FileOutputStream mCacheFile; - - private String mHost; - private int mPort; - private long mContentLenth; - private InputStream mInputStream; - - private static final int MAX_TAGS_CACHE = 100; - - private final MapElement mElement = new MapElement(); - - private static Map tagHash = Collections - .synchronizedMap(new LinkedHashMap( - MAX_TAGS_CACHE, 0.75f, true) { - - private static final long serialVersionUID = 1L; - - @Override - protected boolean removeEldestEntry(Entry e) { - if (size() < MAX_TAGS_CACHE) - return false; - return true; - } - }); - - @Override - public QueryResult executeQuery(MapTile tile, IMapDataSink mapDataSink) { - QueryResult result = QueryResult.SUCCESS; - mCacheFile = null; - - mTile = tile; - - mMapGenerator = mapDataSink; - mCurTagCnt = 0; - - // scale coordinates to tile size - mScaleFactor = REF_TILE_SIZE / Tile.SIZE; - - File f = null; - - mBufferSize = 0; - mBufferPos = 0; - mReadPos = 0; - - if (USE_CACHE) { - f = new File(cacheDir, String.format(CACHE_FILE, - Integer.valueOf(tile.zoomLevel), - Integer.valueOf(tile.tileX), - Integer.valueOf(tile.tileY))); - - if (cacheRead(tile, f)) - return QueryResult.SUCCESS; - } - - try { - if (lwHttpSendRequest(tile) && lwHttpReadHeader() > 0) { - cacheBegin(tile, f); - decode(); - } else { - result = QueryResult.FAILED; - } - } catch (SocketException ex) { - Log.d(TAG, "Socket exception: " + ex.getMessage()); - result = QueryResult.FAILED; - } catch (SocketTimeoutException ex) { - Log.d(TAG, "Socket Timeout exception: " + ex.getMessage()); - result = QueryResult.FAILED; - } catch (UnknownHostException ex) { - Log.d(TAG, "no network"); - result = QueryResult.FAILED; - } catch (Exception ex) { - ex.printStackTrace(); - result = QueryResult.FAILED; - } - - mLastRequest = SystemClock.elapsedRealtime(); - - cacheFinish(tile, f, result == QueryResult.SUCCESS); - - return result; +public class MapDatabase extends ProtobufMapDatabase { + public MapDatabase() { + super(new TileDecoder()); + mConn = new LwHttp("application/osmtile", "osmtile", false); } - - private static File cacheDir; - - @Override - public String getMapProjection() { - return null; - } - - @Override - public MapInfo getMapInfo() { - return mMapInfo; - } - - @Override - public boolean isOpen() { - return mOpenFile; - } - - @Override - public OpenResult open(MapOptions options) { - - if (mOpenFile) - return OpenResult.SUCCESS; - - if (options == null || !options.containsKey("url")) - return new OpenResult("options missing"); - - URL url; - try { - url = new URL(options.get("url")); - } catch (MalformedURLException e) { - - e.printStackTrace(); - return new OpenResult("invalid url: " + options.get("url")); - } - - int port = url.getPort(); - if (port < 0) - port = 80; - - String host = url.getHost(); - String path = url.getPath(); - Log.d(TAG, "open oscim database: " + host + " " + port + " " + path); - - REQUEST_GET_START = ("GET " + path).getBytes(); - REQUEST_GET_END = (".osmtile HTTP/1.1\n" + - "Host: " + host + "\n" + - "Connection: Keep-Alive\n\n").getBytes(); - - mHost = host; - mPort = port; - - //mSockAddr = new InetSocketAddress(host, port); - - mRequestBuffer = new byte[1024]; - System.arraycopy(REQUEST_GET_START, 0, - mRequestBuffer, 0, REQUEST_GET_START.length); - - if (USE_CACHE) { - if (cacheDir == null) { - String externalStorageDirectory = Environment - .getExternalStorageDirectory() - .getAbsolutePath(); - String cacheDirectoryPath = externalStorageDirectory + CACHE_DIRECTORY; - cacheDir = createDirectory(cacheDirectoryPath); - } - } - - mOpenFile = true; - - return OpenResult.SUCCESS; - } - - @Override - public void close() { - mOpenFile = false; - mSockAddr = null; - - if (mSocket != null) { - try { - mSocket.close(); - } catch (IOException e) { - e.printStackTrace(); - } - mSocket = null; - } - - if (USE_CACHE) { - cacheDir = null; - } - } - - private static File createDirectory(String pathName) { - File file = new File(pathName); - if (!file.exists() && !file.mkdirs()) { - throw new IllegalArgumentException("could not create directory: " + file); - } else if (!file.isDirectory()) { - throw new IllegalArgumentException("not a directory: " + file); - } else if (!file.canRead()) { - throw new IllegalArgumentException("cannot read directory: " + file); - } else if (!file.canWrite()) { - throw new IllegalArgumentException("cannot write directory: " + file); - } - return file; - } - - // /////////////// hand sewed tile protocol buffers decoder //////////////// - private static final int TAG_TILE_TAGS = 1; - private static final int TAG_TILE_WAYS = 2; - private static final int TAG_TILE_POLY = 3; - private static final int TAG_TILE_NODES = 4; - private static final int TAG_WAY_TAGS = 11; - private static final int TAG_WAY_INDEX = 12; - private static final int TAG_WAY_COORDS = 13; - private static final int TAG_WAY_LAYER = 21; - private static final int TAG_WAY_NUM_TAGS = 1; - private static final int TAG_WAY_NUM_INDICES = 2; - private static final int TAG_WAY_NUM_COORDS = 3; - - private static final int TAG_NODE_TAGS = 11; - private static final int TAG_NODE_COORDS = 12; - private static final int TAG_NODE_LAYER = 21; - private static final int TAG_NODE_NUM_TAGS = 1; - private static final int TAG_NODE_NUM_COORDS = 2; - - private static final int BUFFER_SIZE = 65536; - - private final byte[] mReadBuffer = new byte[BUFFER_SIZE]; - - // position in read buffer - private int mBufferPos; - // bytes available in read buffer - private int mBufferSize; - // overall bytes of content processed - private int mBytesProcessed; - - - private boolean decode() throws IOException { - mBytesProcessed = 0; - int val; - - while (mBytesProcessed < mContentLenth && (val = decodeVarint32()) > 0) { - // read tag and wire type - int tag = (val >> 3); - - switch (tag) { - case TAG_TILE_TAGS: - decodeTileTags(); - break; - - case TAG_TILE_WAYS: - decodeTileWays(false); - break; - - case TAG_TILE_POLY: - decodeTileWays(true); - break; - - case TAG_TILE_NODES: - decodeTileNodes(); - break; - - default: - Log.d(TAG, "invalid type for tile: " + tag); - return false; - } - } - return true; - } - - private boolean decodeTileTags() throws IOException { - String tagString = decodeString(); - // Log.d(TAG, "tag>" + tagString + "<"); - - if (tagString == null || tagString.length() == 0) { - - curTags[mCurTagCnt++] = new Tag(Tag.TAG_KEY_NAME, "..."); - return false; - } - - Tag tag = tagHash.get(tagString); - - if (tag == null) { - if (tagString.startsWith(Tag.TAG_KEY_NAME)) - tag = new Tag(Tag.TAG_KEY_NAME, tagString.substring(5), false); - else - tag = new Tag(tagString); - - tagHash.put(tagString, tag); - } - - if (mCurTagCnt >= MAX_TILE_TAGS) { - MAX_TILE_TAGS = mCurTagCnt + 10; - Tag[] tmp = new Tag[MAX_TILE_TAGS]; - System.arraycopy(curTags, 0, tmp, 0, mCurTagCnt); - curTags = tmp; - } - curTags[mCurTagCnt++] = tag; - - return true; - } - - private boolean decodeTileWays(boolean polygon) throws IOException { - int bytes = decodeVarint32(); - - int end = mBytesProcessed + bytes; - int indexCnt = 0; - int tagCnt = 0; - int coordCnt = 0; - int layer = 5; - Tag[] tags = null; - short[] index = null; - - boolean skip = false; - boolean fail = false; - - while (mBytesProcessed < end) { - // read tag and wire type - int val = decodeVarint32(); - if (val == 0) - break; - - int tag = (val >> 3); - - switch (tag) { - case TAG_WAY_TAGS: - tags = decodeWayTags(tagCnt); - break; - - case TAG_WAY_INDEX: - index = decodeWayIndices(indexCnt); - break; - - case TAG_WAY_COORDS: - if (coordCnt == 0) - skip = true; - - int cnt = decodeWayCoordinates(skip, coordCnt); - - if (cnt != coordCnt) { - Log.d(TAG, "X wrong number of coordintes"); - fail = true; - } - break; - - case TAG_WAY_LAYER: - layer = decodeVarint32(); - break; - - case TAG_WAY_NUM_TAGS: - tagCnt = decodeVarint32(); - break; - - case TAG_WAY_NUM_INDICES: - indexCnt = decodeVarint32(); - break; - - case TAG_WAY_NUM_COORDS: - coordCnt = decodeVarint32(); - break; - - default: - Log.d(TAG, "X invalid type for way: " + tag); - } - } - - if (fail || index == null || tags == null || indexCnt == 0 || tagCnt == 0) { - Log.d(TAG, "failed reading way: bytes:" + bytes + " index:" - + (tags != null ? tags.toString() : "...") + " " - + indexCnt + " " + coordCnt + " " + tagCnt); - return false; - } - - // FIXME, remove all tiles from cache then remove this below - //if (layer == 0) - // layer = 5; - mElement.type = polygon ? GeometryType.POLY : GeometryType.LINE; - mElement.set(tags, layer); - mMapGenerator.process(mElement); - return true; - } - - private boolean decodeTileNodes() throws IOException { - int bytes = decodeVarint32(); - - int end = mBytesProcessed + bytes; - int tagCnt = 0; - int coordCnt = 0; - byte layer = 0; - Tag[] tags = null; - - while (mBytesProcessed < end) { - // read tag and wire type - int val = decodeVarint32(); - if (val == 0) - break; - - int tag = (val >> 3); - - switch (tag) { - case TAG_NODE_TAGS: - tags = decodeWayTags(tagCnt); - break; - - case TAG_NODE_COORDS: - int cnt = decodeNodeCoordinates(coordCnt, layer, tags); - if (cnt != coordCnt) { - Log.d(TAG, "X wrong number of coordintes"); - return false; - } - break; - - case TAG_NODE_LAYER: - layer = (byte) decodeVarint32(); - break; - - case TAG_NODE_NUM_TAGS: - tagCnt = decodeVarint32(); - break; - - case TAG_NODE_NUM_COORDS: - coordCnt = decodeVarint32(); - break; - - default: - Log.d(TAG, "X invalid type for node: " + tag); - } - } - - return true; - } - - private int decodeNodeCoordinates(int numNodes, byte layer, Tag[] tags) - throws IOException { - int bytes = decodeVarint32(); - - readBuffer(bytes); - int cnt = 0; - int end = mBytesProcessed + bytes; - float scale = mScaleFactor; - // read repeated sint32 - int lastX = 0; - int lastY = 0; - float[] coords = mElement.ensurePointSize(numNodes, false); - - while (mBytesProcessed < end && cnt < numNodes) { - int lon = decodeZigZag32(decodeVarint32()); - int lat = decodeZigZag32(decodeVarint32()); - lastX = lon + lastX; - lastY = lat + lastY; - coords[cnt++] = lastX / scale; - coords[cnt++] = Tile.SIZE - lastY / scale; - } - - - mElement.index[0] = (short)numNodes; - mElement.type = GeometryType.POINT; - mElement.set(tags, layer); - mMapGenerator.process(mElement); - - return cnt; - } - - - private Tag[] decodeWayTags(int tagCnt) throws IOException { - int bytes = decodeVarint32(); - - Tag[] tags = new Tag[tagCnt]; - - int cnt = 0; - int end = mBytesProcessed + bytes; - int max = mCurTagCnt; - - while (mBytesProcessed < end) { - int tagNum = decodeVarint32(); - - if (tagNum < 0 || cnt == tagCnt) { - Log.d(TAG, "NULL TAG: " + mTile + " invalid tag:" + tagNum + " " - + tagCnt + "/" + cnt); - } else { - if (tagNum < Tags.MAX) - tags[cnt++] = Tags.tags[tagNum]; - else { - tagNum -= Tags.LIMIT; - - if (tagNum >= 0 && tagNum < max) { - // Log.d(TAG, "variable tag: " + curTags[tagNum]); - tags[cnt++] = curTags[tagNum]; - } else { - Log.d(TAG, "NULL TAG: " + mTile + " could find tag:" - + tagNum + " " + tagCnt + "/" + cnt); - } - } - } - } - - if (tagCnt != cnt) - Log.d(TAG, "NULL TAG: " + mTile + " ..."); - - return tags; - } - - private short[] decodeWayIndices(int indexCnt) throws IOException { - int bytes = decodeVarint32(); - - short[] index = mElement.ensureIndexSize(indexCnt + 1, false); - - readBuffer(bytes); - - int cnt = 0; - // int end = bytesRead + bytes; - - int pos = mBufferPos; - int end = pos + bytes; - byte[] buf = mReadBuffer; - int result; - - while (pos < end) { - // int val = decodeVarint32(); - - if (buf[pos] >= 0) { - result = buf[pos++]; - } else if (buf[pos + 1] >= 0) { - result = (buf[pos] & 0x7f) - | buf[pos + 1] << 7; - pos += 2; - } else if (buf[pos + 2] >= 0) { - result = (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2]) << 14; - pos += 3; - } else if (buf[pos + 3] >= 0) { - result = (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2] & 0x7f) << 14 - | (buf[pos + 3]) << 21; - pos += 4; - } else { - result = (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2] & 0x7f) << 14 - | (buf[pos + 3] & 0x7f) << 21 - | (buf[pos + 4]) << 28; - - pos += 4; - int i = 0; - - while (buf[pos++] < 0 && i < 10) - i++; - - if (i == 10) - throw new IOException("X malformed VarInt32"); - - } - - index[cnt++] = (short) (result * 2); - - // if (cnt < indexCnt) - // index[cnt++] = (short) (val * 2); - // else DEBUG... - - } - - mBufferPos = pos; - mBytesProcessed += bytes; - - index[indexCnt] = -1; - - return index; - } - - private int decodeWayCoordinates(boolean skip, int nodes) throws IOException { - int bytes = decodeVarint32(); - - readBuffer(bytes); - - if (skip) { - mBufferPos += bytes; - return nodes; - } - - int pos = mBufferPos; - int end = pos + bytes; - byte[] buf = mReadBuffer; - int cnt = 0; - int result; - - float scale = mScaleFactor; - int x, lastX = 0; - int y, lastY = 0; - boolean even = true; - - float[] coords = mElement.ensurePointSize(nodes, false); - - // read repeated sint32 - while (pos < end) { - - if (buf[pos] >= 0) { - result = buf[pos++]; - } else if (buf[pos + 1] >= 0) { - result = (buf[pos] & 0x7f) - | buf[pos + 1] << 7; - pos += 2; - } else if (buf[pos + 2] >= 0) { - result = (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2]) << 14; - pos += 3; - } else if (buf[pos + 3] >= 0) { - result = (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2] & 0x7f) << 14 - | (buf[pos + 3]) << 21; - pos += 4; - } else { - result = (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2] & 0x7f) << 14 - | (buf[pos + 3] & 0x7f) << 21 - | (buf[pos + 4]) << 28; - - pos += 4; - int i = 0; - - while (buf[pos++] < 0 && i < 10) - i++; - - if (i == 10) - throw new IOException("X malformed VarInt32"); - - } - if (even) { - x = ((result >>> 1) ^ -(result & 1)); - lastX = lastX + x; - coords[cnt++] = lastX / scale; - even = false; - } else { - y = ((result >>> 1) ^ -(result & 1)); - lastY = lastY + y; - coords[cnt++] = Tile.SIZE - lastY / scale; - even = true; - } - } - - mBufferPos = pos; - mBytesProcessed += bytes; - - return cnt; - } - - int mReadPos; - - private int readBuffer(int size) throws IOException { - int read = 0; - - if (mBufferPos + size < mBufferSize) - return mBufferSize - mBufferPos; - - if (mReadPos == mContentLenth) - return mBufferSize - mBufferPos; - - if (size > BUFFER_SIZE) { - // FIXME throw exception for now, but frankly better - // sanitize tile data on compilation. this should only - // happen with strings or one ways coordinates are - // larger than 64kb - throw new IOException("X requested size too large " + mTile); - } - - if (mBufferSize == mBufferPos) { - mBufferPos = 0; - mBufferSize = 0; - } else if (mBufferPos + size > BUFFER_SIZE) { - //Log.d(TAG, "wrap buffer" + (size - mBufferSize) + " " + mBufferPos); - // copy bytes left to read to the beginning of buffer - mBufferSize -= mBufferPos; - System.arraycopy(mReadBuffer, mBufferPos, mReadBuffer, 0, mBufferSize); - mBufferPos = 0; - } - - int max = BUFFER_SIZE - mBufferSize; - - while ((mBufferSize - mBufferPos) < size && max > 0) { - - max = BUFFER_SIZE - mBufferSize; - if (max > mContentLenth - mReadPos) - max = (int) (mContentLenth - mReadPos); - - // read until requested size is available in buffer - int len = mInputStream.read(mReadBuffer, mBufferSize, max); - - if (len < 0) { - // finished reading, mark end - mReadBuffer[mBufferSize] = 0; - break; - } - - read += len; - mReadPos += len; - - if (mCacheFile != null) - mCacheFile.write(mReadBuffer, mBufferSize, len); - - // if (USE_LW_HTTP) { - if (mReadPos == mContentLenth) - break; - // } - - mBufferSize += len; - } - - return read; - } - - @Override - public void cancel() { - // if (mRequest != null) { - // mRequest.abort(); - // mRequest = null; - // } - } - - private int decodeVarint32() throws IOException { - int pos = mBufferPos; - - if (pos + 10 > mBufferSize) { - readBuffer(4096); - pos = mBufferPos; - } - - byte[] buf = mReadBuffer; - - if (buf[pos] >= 0) { - mBufferPos += 1; - mBytesProcessed += 1; - return buf[pos]; - } else if (buf[pos + 1] >= 0) { - mBufferPos += 2; - mBytesProcessed += 2; - return (buf[pos] & 0x7f) - | (buf[pos + 1]) << 7; - - } else if (buf[pos + 2] >= 0) { - mBufferPos += 3; - mBytesProcessed += 3; - return (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2]) << 14; - } else if (buf[pos + 3] >= 0) { - mBufferPos += 4; - mBytesProcessed += 4; - return (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2] & 0x7f) << 14 - | (buf[pos + 3]) << 21; - } - - int result = (buf[pos] & 0x7f) - | (buf[pos + 1] & 0x7f) << 7 - | (buf[pos + 2] & 0x7f) << 14 - | (buf[pos + 3] & 0x7f) << 21 - | (buf[pos + 4]) << 28; - - int read = 5; - pos += 4; - - // 'Discard upper 32 bits' - the original comment. - // havent found this in any document but the code provided by google. - while (buf[pos++] < 0 && read < 10) - read++; - - if (read == 10) - throw new IOException("X malformed VarInt32"); - - mBufferPos += read; - mBytesProcessed += read; - - return result; - } - - // ///////////////////////// Lightweight HttpClient ////////////////////// - // would have written simple tcp server/client for this... - - private int mMaxReq = 0; - private Socket mSocket; - private OutputStream mCommandStream; - private InputStream mResponseStream; - private long mLastRequest = 0; - private SocketAddress mSockAddr; - - private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes(); - private final static byte[] RESPONSE_CONTENT_LEN = "Content-Length: ".getBytes(); - private final static int RESPONSE_EXPECTED_LIVES = 100; - private final static int RESPONSE_EXPECTED_TIMEOUT = 10000; - - private byte[] REQUEST_GET_START;// = "GET /osmstache/test/".getBytes(); - private byte[] REQUEST_GET_END; - // = (".osmtile HTTP/1.1\n" + - //"Host: " + SERVER_ADDR + "\n" + - //"Connection: Keep-Alive\n\n").getBytes(); - - private byte[] mRequestBuffer; - - int lwHttpReadHeader() throws IOException { - InputStream is = mResponseStream; - - byte[] buf = mReadBuffer; - - int read = 0; - int pos = 0; - int end = 0; - // int max_req = 0; - int resp_len = 0; - boolean first = true; - - for (int len = 0; pos < read - || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) { - read += len; - - while (end < read && (buf[end] != '\n')) - end++; - - if (buf[end] == '\n') { - - if (first) { - // check for OK - for (int i = 0; i < 15 && pos + i < end; i++) - if (buf[pos + i] != RESPONSE_HTTP_OK[i]) - return -1; - first = false; - } else if (end - pos == 1) { - // check empty line (header end) - end += 1; - break; - } - else { - // parse Content-Length, TODO just encode this with message - for (int i = 0; pos + i < end - 1; i++) { - if (i < 16) { - if (buf[pos + i] == RESPONSE_CONTENT_LEN[i]) - continue; - - break; - } - - // read int value - resp_len = resp_len * 10 + (buf[pos + i]) - '0'; - } - } - - // String line = new String(buf, pos, end - pos - 1); - // Log.d(TAG, ">" + line + "< " + resp_len); - - pos += (end - pos) + 1; - end = pos; - } - } - - mContentLenth = resp_len; - - // start of content - mBufferPos = end; - - // bytes of content already read into buffer - mReadPos = read - end; - - // buffer fill - mBufferSize = read; - - mInputStream = mResponseStream; - - return resp_len; - } - - private boolean lwHttpSendRequest(Tile tile) throws IOException { - if (mSockAddr == null) { - mSockAddr = new InetSocketAddress(mHost, mPort); - } - - if (mSocket != null && ((mMaxReq-- <= 0) - || (SystemClock.elapsedRealtime() - mLastRequest - > RESPONSE_EXPECTED_TIMEOUT))) { - - try { - mSocket.close(); - } catch (IOException e) { - - } - - // Log.d(TAG, "not alive - recreate connection " + mMaxReq); - mSocket = null; - } - - if (mSocket == null) { - lwHttpConnect(); - // we know our server - mMaxReq = RESPONSE_EXPECTED_LIVES; - // Log.d(TAG, "create connection"); - } else { - // should not be needed - int avail = mResponseStream.available(); - if (avail > 0) { - Log.d(TAG, "Consume left-over bytes: " + avail); - mResponseStream.read(mReadBuffer, 0, avail); - } - } - - byte[] request = mRequestBuffer; - int pos = REQUEST_GET_START.length; - - pos = writeInt(tile.zoomLevel, pos, request); - request[pos++] = '/'; - pos = writeInt(tile.tileX, pos, request); - request[pos++] = '/'; - pos = writeInt(tile.tileY, pos, request); - - int len = REQUEST_GET_END.length; - System.arraycopy(REQUEST_GET_END, 0, request, pos, len); - len += pos; - - // this does the same but with a few more allocations: - // byte[] request = String.format(REQUEST, - // Integer.valueOf(tile.zoomLevel), - // Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes(); - - try { - mCommandStream.write(request, 0, len); - mCommandStream.flush(); - return true; - } catch (IOException e) { - Log.d(TAG, "retry - recreate connection"); - } - - lwHttpConnect(); - - mCommandStream.write(request, 0, len); - mCommandStream.flush(); - - return true; - } - - private boolean lwHttpConnect() throws IOException { - // if (mRequestBuffer == null) { - // mRequestBuffer = new byte[1024]; - // System.arraycopy(REQUEST_GET_START, - // 0, mRequestBuffer, 0, - // REQUEST_GET_START.length); - // } - - mSocket = new Socket(); - mSocket.connect(mSockAddr, 30000); - mSocket.setTcpNoDelay(true); - // mCmdBuffer = new PrintStream(mSocket.getOutputStream()); - mCommandStream = new BufferedOutputStream(mSocket.getOutputStream()); - mResponseStream = mSocket.getInputStream(); - return true; - } - - // write (positive) integer as char sequence to buffer - private static int writeInt(int val, int pos, byte[] buf) { - if (val == 0) { - buf[pos] = '0'; - return pos + 1; - } - - int i = 0; - for (int n = val; n > 0; n = n / 10, i++) - buf[pos + i] = (byte) ('0' + n % 10); - - // reverse bytes - for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) { - byte tmp = buf[j]; - buf[j] = buf[end]; - buf[end] = tmp; - } - - return pos + i; - - } - - // //////////////////////////// Tile cache /////////////////////////////// - - private boolean cacheRead(Tile tile, File f) { - if (f.exists() && f.length() > 0) { - FileInputStream in; - - try { - in = new FileInputStream(f); - - mContentLenth = f.length(); - Log.d(TAG, tile + " using cache: " + mContentLenth); - mInputStream = in; - - decode(); - in.close(); - - return true; - - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (Exception ex) { - ex.printStackTrace(); - } - - f.delete(); - return false; - } - - return false; - } - - private boolean cacheBegin(Tile tile, File f) { - if (USE_CACHE) { - try { - Log.d(TAG, "writing cache: " + tile); - mCacheFile = new FileOutputStream(f); - - if (mReadPos > 0) { - try { - mCacheFile.write(mReadBuffer, mBufferPos, - mBufferSize - mBufferPos); - - } catch (IOException e) { - e.printStackTrace(); - mCacheFile = null; - return false; - } - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - mCacheFile = null; - return false; - } - } - return true; - } - - private void cacheFinish(Tile tile, File file, boolean success) { - if (USE_CACHE) { - if (success) { - try { - mCacheFile.flush(); - mCacheFile.close(); - Log.d(TAG, tile + " cache written " + file.length()); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - file.delete(); - } - } - mCacheFile = null; - } - - /* - * All code below is taken from or based on Google's Protocol Buffers - * implementation: - */ - - // Protocol Buffers - Google's data interchange format - // Copyright 2008 Google Inc. All rights reserved. - // http://code.google.com/p/protobuf/ - // - // Redistribution and use in source and binary forms, with or without - // modification, are permitted provided that the following conditions are - // met: - // - // * Redistributions of source code must retain the above copyright - // notice, this list of conditions and the following disclaimer. - // * Redistributions in binary form must reproduce the above - // copyright notice, this list of conditions and the following disclaimer - // in the documentation and/or other materials provided with the - // distribution. - // * Neither the name of Google Inc. nor the names of its - // contributors may be used to endorse or promote products derived from - // this software without specific prior written permission. - // - // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - private String decodeString() throws IOException { - final int size = decodeVarint32(); - - readBuffer(size); - - final String result = new String(mReadBuffer, mBufferPos, size, "UTF-8"); - - mBufferPos += size; - mBytesProcessed += size; - return result; - - } - - private static int decodeZigZag32(final int n) { - return (n >>> 1) ^ -(n & 1); - } - } diff --git a/src/org/oscim/database/oscimap/TileDecoder.java b/src/org/oscim/database/oscimap/TileDecoder.java new file mode 100644 index 00000000..124afa71 --- /dev/null +++ b/src/org/oscim/database/oscimap/TileDecoder.java @@ -0,0 +1,449 @@ +/* + * Copyright 2013 + * + * 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 Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along with + * this program. If not, see . + */ +package org.oscim.database.oscimap; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.oscim.core.GeometryBuffer.GeometryType; +import org.oscim.core.MapElement; +import org.oscim.core.Tag; +import org.oscim.core.Tile; +import org.oscim.database.IMapDataSink; +import org.oscim.database.common.ProtobufDecoder; + +import android.util.Log; + +public class TileDecoder extends ProtobufDecoder { + private final static String TAG = TileDecoder.class.getName(); + + private final static float REF_TILE_SIZE = 4096.0f; + + private static final int TAG_TILE_TAGS = 1; + private static final int TAG_TILE_WAYS = 2; + private static final int TAG_TILE_POLY = 3; + private static final int TAG_TILE_NODES = 4; + private static final int TAG_WAY_TAGS = 11; + private static final int TAG_WAY_INDEX = 12; + private static final int TAG_WAY_COORDS = 13; + private static final int TAG_WAY_LAYER = 21; + private static final int TAG_WAY_NUM_TAGS = 1; + private static final int TAG_WAY_NUM_INDICES = 2; + private static final int TAG_WAY_NUM_COORDS = 3; + + private static final int TAG_NODE_TAGS = 11; + private static final int TAG_NODE_COORDS = 12; + private static final int TAG_NODE_LAYER = 21; + private static final int TAG_NODE_NUM_TAGS = 1; + private static final int TAG_NODE_NUM_COORDS = 2; + + private int MAX_TILE_TAGS = 100; + private Tag[] curTags = new Tag[MAX_TILE_TAGS]; + private int mCurTagCnt; + + private IMapDataSink mSink; + private float mScale; + private Tile mTile; + private final MapElement mElem; + + TileDecoder() { + mElem = new MapElement(); + } + + @Override + public boolean decode(Tile tile, IMapDataSink sink, InputStream is, int contentLength) + throws IOException { + + + setInputStream(is, contentLength); + + mTile = tile; + mSink = sink; + mScale = REF_TILE_SIZE / Tile.SIZE; + return decode(); + } + + private static final int MAX_TAGS_CACHE = 100; + private static Map tagHash = Collections + .synchronizedMap(new LinkedHashMap( + MAX_TAGS_CACHE, 0.75f, true) { + + private static final long serialVersionUID = 1L; + + @Override + protected boolean removeEldestEntry(Entry e) { + if (size() < MAX_TAGS_CACHE) + return false; + return true; + } + }); + + private boolean decode() throws IOException { + int val; + mCurTagCnt = 0; + + while (hasData() && (val = decodeVarint32()) > 0) { + // read tag and wire type + int tag = (val >> 3); + + switch (tag) { + case TAG_TILE_TAGS: + decodeTileTags(); + break; + + case TAG_TILE_WAYS: + decodeTileWays(false); + break; + + case TAG_TILE_POLY: + decodeTileWays(true); + break; + + case TAG_TILE_NODES: + decodeTileNodes(); + break; + + default: + Log.d(TAG, "invalid type for tile: " + tag); + return false; + } + } + return true; + } + + private boolean decodeTileTags() throws IOException { + String tagString = decodeString(); + + if (tagString == null || tagString.length() == 0) { + curTags[mCurTagCnt++] = new Tag(Tag.TAG_KEY_NAME, "..."); + return false; + } + + Tag tag = tagHash.get(tagString); + + if (tag == null) { + if (tagString.startsWith(Tag.TAG_KEY_NAME)) + tag = new Tag(Tag.TAG_KEY_NAME, tagString.substring(5), false); + else + tag = new Tag(tagString); + + tagHash.put(tagString, tag); + } + + if (mCurTagCnt >= MAX_TILE_TAGS) { + MAX_TILE_TAGS = mCurTagCnt + 10; + Tag[] tmp = new Tag[MAX_TILE_TAGS]; + System.arraycopy(curTags, 0, tmp, 0, mCurTagCnt); + curTags = tmp; + } + curTags[mCurTagCnt++] = tag; + + return true; + } + + private boolean decodeTileWays(boolean polygon) throws IOException { + int bytes = decodeVarint32(); + + int end = position() + bytes; + int indexCnt = 0; + int tagCnt = 0; + int coordCnt = 0; + int layer = 5; + Tag[] tags = null; + + boolean fail = false; + + while (position() < end) { + // read tag and wire type + int val = decodeVarint32(); + if (val == 0) + break; + + int tag = (val >> 3); + + switch (tag) { + case TAG_WAY_TAGS: + tags = decodeWayTags(tagCnt); + break; + + case TAG_WAY_INDEX: + //index = + decodeWayIndices(indexCnt); + break; + + case TAG_WAY_COORDS: + if (coordCnt == 0) { + Log.d(TAG, mTile + " no coordinates"); + } + + mElem.ensurePointSize(coordCnt, false); + int cnt = decodeInterleavedPoints(mElem.points, mScale); + + if (cnt != coordCnt) { + Log.d(TAG, mTile + " wrong number of coordintes " + + coordCnt + "/" + cnt); + fail = true; + } + + break; + + case TAG_WAY_LAYER: + layer = decodeVarint32(); + break; + + case TAG_WAY_NUM_TAGS: + tagCnt = decodeVarint32(); + break; + + case TAG_WAY_NUM_INDICES: + indexCnt = decodeVarint32(); + break; + + case TAG_WAY_NUM_COORDS: + coordCnt = decodeVarint32(); + break; + + default: + Log.d(TAG, "X invalid type for way: " + tag); + } + } + + if (fail || tags == null || indexCnt == 0 || tagCnt == 0) { + Log.d(TAG, "failed reading way: bytes:" + bytes + " index:" + + (tags != null ? tags.toString() : "...") + " " + + indexCnt + " " + coordCnt + " " + tagCnt); + return false; + } + + // FIXME, remove all tiles from cache then remove this below + //if (layer == 0) + // layer = 5; + mElem.type = polygon ? GeometryType.POLY : GeometryType.LINE; + mElem.set(tags, layer); + mSink.process(mElem); + return true; + } + + private boolean decodeTileNodes() throws IOException { + int bytes = decodeVarint32(); + + int end = position() + bytes; + int tagCnt = 0; + int coordCnt = 0; + byte layer = 0; + Tag[] tags = null; + + while (position() < end) { + // read tag and wire type + int val = decodeVarint32(); + if (val == 0) + break; + + int tag = (val >> 3); + + switch (tag) { + case TAG_NODE_TAGS: + tags = decodeWayTags(tagCnt); + break; + + case TAG_NODE_COORDS: + int cnt = decodeNodeCoordinates(coordCnt, layer, tags); + if (cnt != coordCnt) { + Log.d(TAG, "X wrong number of coordintes"); + return false; + } + break; + + case TAG_NODE_LAYER: + layer = (byte) decodeVarint32(); + break; + + case TAG_NODE_NUM_TAGS: + tagCnt = decodeVarint32(); + break; + + case TAG_NODE_NUM_COORDS: + coordCnt = decodeVarint32(); + break; + + default: + Log.d(TAG, "X invalid type for node: " + tag); + } + } + + return true; + } + + private int decodeNodeCoordinates(int numNodes, byte layer, Tag[] tags) + throws IOException { + int bytes = decodeVarint32(); + + fillBuffer(bytes); + int cnt = 0; + int end = position() + bytes; + // read repeated sint32 + int lastX = 0; + int lastY = 0; + float[] coords = mElem.ensurePointSize(numNodes, false); + + while (position() < end && cnt < numNodes) { + int lon = deZigZag(decodeVarint32()); + int lat = deZigZag(decodeVarint32()); + lastX = lon + lastX; + lastY = lat + lastY; + coords[cnt++] = lastX / mScale; + coords[cnt++] = Tile.SIZE - lastY / mScale; + } + + mElem.index[0] = (short) numNodes; + mElem.type = GeometryType.POINT; + mElem.set(tags, layer); + mSink.process(mElem); + + return cnt; + } + + private Tag[] decodeWayTags(int tagCnt) throws IOException { + int bytes = decodeVarint32(); + + Tag[] tags = new Tag[tagCnt]; + + int cnt = 0; + int end = position() + bytes; + int max = mCurTagCnt; + + while (position() < end) { + int tagNum = decodeVarint32(); + + if (tagNum < 0 || cnt == tagCnt) { + Log.d(TAG, "NULL TAG: " + mTile + " invalid tag:" + tagNum + + " " + tagCnt + "/" + cnt); + } else { + if (tagNum < Tags.MAX) + tags[cnt++] = Tags.tags[tagNum]; + else { + tagNum -= Tags.LIMIT; + + if (tagNum >= 0 && tagNum < max) { + // Log.d(TAG, "variable tag: " + curTags[tagNum]); + tags[cnt++] = curTags[tagNum]; + } else { + Log.d(TAG, "NULL TAG: " + mTile + " could find tag:" + + tagNum + " " + tagCnt + "/" + cnt); + } + } + } + } + + if (tagCnt != cnt) + Log.d(TAG, "NULL TAG: " + mTile); + + return tags; + } + + private int decodeWayIndices(int indexCnt) throws IOException { + mElem.ensureIndexSize(indexCnt, false); + + decodeVarintArray(indexCnt, mElem.index); + + short[] index = mElem.index; + int coordCnt = 0; + + for (int i = 0; i < indexCnt; i++) { + coordCnt += index[i]; + index[i] *= 2; + } + + // set end marker + if (indexCnt < index.length) + index[indexCnt] = -1; + + return coordCnt; + } + + @Override + protected int decodeInterleavedPoints(float[] coords, float scale) + throws IOException { + + int bytes = decodeVarint32(); + fillBuffer(bytes); + + int cnt = 0; + int lastX = 0; + int lastY = 0; + boolean even = true; + + byte[] buf = buffer; + int pos = bufferPos; + int end = pos + bytes; + int val; + + while (pos < end) { + if (buf[pos] >= 0) { + val = buf[pos++]; + + } else if (buf[pos + 1] >= 0) { + val = (buf[pos++] & 0x7f) + | buf[pos++] << 7; + + } else if (buf[pos + 2] >= 0) { + val = (buf[pos++] & 0x7f) + | (buf[pos++] & 0x7f) << 7 + | (buf[pos++]) << 14; + + } else if (buf[pos + 3] >= 0) { + val = (buf[pos++] & 0x7f) + | (buf[pos++] & 0x7f) << 7 + | (buf[pos++] & 0x7f) << 14 + | (buf[pos++]) << 21; + + } else { + val = (buf[pos++] & 0x7f) + | (buf[pos++] & 0x7f) << 7 + | (buf[pos++] & 0x7f) << 14 + | (buf[pos++] & 0x7f) << 21 + | (buf[pos]) << 28; + + if (buf[pos++] < 0) + throw INVALID_VARINT; + } + + // zigzag decoding + int s = ((val >>> 1) ^ -(val & 1)); + + if (even) { + lastX = lastX + s; + coords[cnt++] = lastX / scale; + even = false; + } else { + lastY = lastY + s; + coords[cnt++] = Tile.SIZE - lastY / scale; + even = true; + } + } + + if (pos != bufferPos + bytes) + throw INVALID_PACKED_SIZE; + + bufferPos = pos; + + // return number of points read + return cnt; + } + +} diff --git a/src/org/oscim/database/oscimap2/LwHttp.java b/src/org/oscim/database/oscimap2/LwHttp.java deleted file mode 100644 index 7dd31dde..00000000 --- a/src/org/oscim/database/oscimap2/LwHttp.java +++ /dev/null @@ -1,450 +0,0 @@ -/* - * Copyright 2013 Hannes Janetzek - * - * 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 Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see . - */ -package org.oscim.database.oscimap2; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.InetSocketAddress; -import java.net.MalformedURLException; -import java.net.Socket; -import java.net.SocketAddress; -import java.net.URL; - -import org.oscim.core.Tile; - -import android.os.SystemClock; -import android.util.Log; - -public class LwHttp { - private static final String TAG = LwHttp.class.getName(); - private final static int BUFFER_SIZE = 65536; - - // - byte[] buffer = new byte[BUFFER_SIZE]; - - // position in buffer - int bufferPos; - - // bytes available in buffer - int bufferFill; - - - // offset of buffer in message - private int mBufferOffset; - - // max bytes to read: message = header + content - private long mReadEnd; - - // overall bytes of message read - private int mReadPos; - - private String mHost; - private int mPort; - private InputStream mInputStream; - - private int mMaxReq = 0; - private Socket mSocket; - private OutputStream mCommandStream; - private InputStream mResponseStream; - long mLastRequest = 0; - private SocketAddress mSockAddr; - - private final static byte[] RESPONSE_HTTP_OK = "HTTP/1.1 200 OK".getBytes(); - private final static int RESPONSE_EXPECTED_LIVES = 100; - private final static int RESPONSE_EXPECTED_TIMEOUT = 10000; - - private byte[] REQUEST_GET_START; - private byte[] REQUEST_GET_END; - - private byte[] mRequestBuffer; - - boolean setServer(String urlString) { - URL url; - try { - url = new URL(urlString); - } catch (MalformedURLException e) { - - e.printStackTrace(); - return false; - //return new OpenResult("invalid url: " + options.get("url")); - } - - int port = url.getPort(); - if (port < 0) - port = 80; - - String host = url.getHost(); - String path = url.getPath(); - Log.d(TAG, "open oscim database: " + host + " " + port + " " + path); - - REQUEST_GET_START = ("GET " + path).getBytes(); - REQUEST_GET_END = (".osmtile HTTP/1.1\n" + - "Host: " + host + "\n" + - "Connection: Keep-Alive\n\n").getBytes(); - - mHost = host; - mPort = port; - - mRequestBuffer = new byte[1024]; - System.arraycopy(REQUEST_GET_START, 0, - mRequestBuffer, 0, REQUEST_GET_START.length); - return true; - } - - void close() { - if (mSocket != null) { - try { - mSocket.close(); - } catch (IOException e) { - e.printStackTrace(); - } finally { - mSocket = null; - } - } - } - - int readHeader() throws IOException { - InputStream is = mResponseStream; - - byte[] buf = buffer; - boolean first = true; - int read = 0; - int pos = 0; - int end = 0; - int len = 0; - - // header cannot be larger than BUFFER_SIZE for this to work - for (; pos < read || (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0; len = 0) { - read += len; - while (end < read && (buf[end] != '\n')) - end++; - - if (buf[end] == '\n') { - if (first) { - // check only for OK - first = false; - if (!compareBytes(buf, pos, end, RESPONSE_HTTP_OK, 15)) - return -1; - - } else if (end - pos == 1) { - // check empty line (header end) - end += 1; - break; - } - - // String line = new String(buf, pos, end - pos - 1); - // Log.d(TAG, ">" + line + "< " + resp_len); - - pos += (end - pos) + 1; - end = pos; - } - } - - // check 4 bytes available.. - while ((read - end) < 4 && (len = is.read(buf, read, BUFFER_SIZE - read)) >= 0) - read += len; - - if (read - len < 4) - return -1; - - int contentLength = decodeInt(buf, end); - - // start of content - bufferPos = end + 4; - // buffer fill - bufferFill = read; - mBufferOffset = 0; - - // overall bytes of already read - mReadPos = read; - mReadEnd = bufferPos + contentLength; - - mInputStream = mResponseStream; - - return 1; - } - - boolean sendRequest(Tile tile) throws IOException { - - bufferFill = 0; - bufferPos = 0; - mReadPos = 0; - mCacheFile = null; - - if (mSocket != null && ((mMaxReq-- <= 0) - || (SystemClock.elapsedRealtime() - mLastRequest - > RESPONSE_EXPECTED_TIMEOUT))) { - try { - mSocket.close(); - } catch (IOException e) { - - } - - // Log.d(TAG, "not alive - recreate connection " + mMaxReq); - mSocket = null; - } - - if (mSocket == null) { - lwHttpConnect(); - // we know our server - mMaxReq = RESPONSE_EXPECTED_LIVES; - // Log.d(TAG, "create connection"); - } else { - // should not be needed - int avail = mResponseStream.available(); - if (avail > 0) { - Log.d(TAG, "Consume left-over bytes: " + avail); - mResponseStream.read(buffer, 0, avail); - } - } - - byte[] request = mRequestBuffer; - int pos = REQUEST_GET_START.length; - - pos = writeInt(tile.zoomLevel, pos, request); - request[pos++] = '/'; - pos = writeInt(tile.tileX, pos, request); - request[pos++] = '/'; - pos = writeInt(tile.tileY, pos, request); - - int len = REQUEST_GET_END.length; - System.arraycopy(REQUEST_GET_END, 0, request, pos, len); - len += pos; - - // this does the same but with a few more allocations: - // byte[] request = String.format(REQUEST, - // Integer.valueOf(tile.zoomLevel), - // Integer.valueOf(tile.tileX), Integer.valueOf(tile.tileY)).getBytes(); - - try { - mCommandStream.write(request, 0, len); - mCommandStream.flush(); - return true; - } catch (IOException e) { - Log.d(TAG, "recreate connection"); - } - - lwHttpConnect(); - - mCommandStream.write(request, 0, len); - mCommandStream.flush(); - - return true; - } - - private boolean lwHttpConnect() throws IOException { - if (mSockAddr == null) - mSockAddr = new InetSocketAddress(mHost, mPort); - - mSocket = new Socket(); - mSocket.connect(mSockAddr, 30000); - mSocket.setTcpNoDelay(true); - - mCommandStream = mSocket.getOutputStream(); //new BufferedOutputStream(); - mResponseStream = mSocket.getInputStream(); - - return true; - } - - // write (positive) integer as char sequence to buffer - private static int writeInt(int val, int pos, byte[] buf) { - if (val == 0) { - buf[pos] = '0'; - return pos + 1; - } - - int i = 0; - for (int n = val; n > 0; n = n / 10, i++) - buf[pos + i] = (byte) ('0' + n % 10); - - // reverse bytes - for (int j = pos, end = pos + i - 1, mid = pos + i / 2; j < mid; j++, end--) { - byte tmp = buf[j]; - buf[j] = buf[end]; - buf[end] = tmp; - } - - return pos + i; - } - - private static boolean compareBytes(byte[] buffer, int position, int available, - byte[] string, int length) { - - if (available - position < length) - return false; - - for (int i = 0; i < length; i++) - if (buffer[position + i] != string[i]) - return false; - - return true; - } - - static int decodeInt(byte[] buffer, int offset) { - return buffer[offset] << 24 | (buffer[offset + 1] & 0xff) << 16 - | (buffer[offset + 2] & 0xff) << 8 - | (buffer[offset + 3] & 0xff); - } - - public boolean hasData() { - return mBufferOffset + bufferPos < mReadEnd; - } - - public int position() { - return mBufferOffset + bufferPos; - } - - public void readBuffer(int size) throws IOException { - // check if buffer already contains the request bytes - if (bufferPos + size < bufferFill) - return; - - // check if inputstream is read to the end - if (mReadPos == mReadEnd) - return; - - int maxSize = buffer.length; - - if (size > maxSize) { - Log.d(TAG, "increase read buffer to " + size + " bytes"); - maxSize = size; - byte[] tmp = new byte[maxSize]; - bufferFill -= bufferPos; - System.arraycopy(buffer, bufferPos, tmp, 0, bufferFill); - mBufferOffset += bufferPos; - bufferPos = 0; - buffer = tmp; - } - - if (bufferFill == bufferPos) { - mBufferOffset += bufferPos; - bufferPos = 0; - bufferFill = 0; - } else if (bufferPos + size > maxSize) { - // copy bytes left to the beginning of buffer - bufferFill -= bufferPos; - System.arraycopy(buffer, bufferPos, buffer, 0, bufferFill); - mBufferOffset += bufferPos; - bufferPos = 0; - } - - int max = maxSize - bufferFill; - - while ((bufferFill - bufferPos) < size && max > 0) { - - max = maxSize - bufferFill; - if (max > mReadEnd - mReadPos) - max = (int) (mReadEnd - mReadPos); - - // read until requested size is available in buffer - int len = mInputStream.read(buffer, bufferFill, max); - - if (len < 0) { - // finished reading, mark end - buffer[bufferFill] = 0; - break; - } - - mReadPos += len; - - // if (mCacheFile != null) - // mCacheFile.write(mReadBuffer, mBufferFill, len); - - if (mReadPos == mReadEnd) - break; - - bufferFill += len; - } - } - - private FileOutputStream mCacheFile; - - boolean cacheRead(Tile tile, File f) { - if (f.exists() && f.length() > 0) { - FileInputStream in; - - try { - in = new FileInputStream(f); - - mReadEnd = f.length(); - Log.d(TAG, tile + " - using cache: " + mReadEnd); - mInputStream = in; - - //decode(); - in.close(); - - return true; - - } catch (FileNotFoundException e) { - e.printStackTrace(); - } catch (Exception ex) { - ex.printStackTrace(); - } - - f.delete(); - return false; - } - - return false; - } - - boolean cacheBegin(Tile tile, File f) { - if (MapDatabase.USE_CACHE) { - try { - Log.d(TAG, tile + " - writing cache"); - mCacheFile = new FileOutputStream(f); - - if (mReadPos > 0) { - try { - mCacheFile.write(buffer, bufferPos, - bufferFill - bufferPos); - - } catch (IOException e) { - e.printStackTrace(); - mCacheFile = null; - return false; - } - } - } catch (FileNotFoundException e) { - e.printStackTrace(); - mCacheFile = null; - return false; - } - } - return true; - } - - void cacheFinish(Tile tile, File file, boolean success) { - if (MapDatabase.USE_CACHE) { - if (success) { - try { - mCacheFile.flush(); - mCacheFile.close(); - Log.d(TAG, tile + " - cache written " + file.length()); - } catch (IOException e) { - e.printStackTrace(); - } - } else { - file.delete(); - } - } - mCacheFile = null; - } -} diff --git a/src/org/oscim/database/oscimap2/MapDatabase.java b/src/org/oscim/database/oscimap2/MapDatabase.java index b8d3569e..a437490a 100644 --- a/src/org/oscim/database/oscimap2/MapDatabase.java +++ b/src/org/oscim/database/oscimap2/MapDatabase.java @@ -14,652 +14,322 @@ */ package org.oscim.database.oscimap2; -import java.io.File; import java.io.IOException; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; +import java.io.InputStream; import java.util.Arrays; -import org.oscim.core.BoundingBox; -import org.oscim.core.GeoPoint; import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.MapElement; import org.oscim.core.Tag; import org.oscim.core.Tile; -import org.oscim.database.IMapDatabase; import org.oscim.database.IMapDataSink; -import org.oscim.database.MapInfo; -import org.oscim.database.MapOptions; -import org.oscim.layers.tile.MapTile; -import org.oscim.utils.UTF8Decoder; +import org.oscim.database.common.LwHttp; +import org.oscim.database.common.ProtobufDecoder; +import org.oscim.database.common.ProtobufMapDatabase; -import android.os.Environment; -import android.os.SystemClock; import android.util.Log; /** - * - * + * Current Protocol Implementation */ -public class MapDatabase implements IMapDatabase { - private static final String TAG = MapDatabase.class.getName(); - - static final boolean USE_CACHE = false; - - private static final MapInfo mMapInfo = - new MapInfo(new BoundingBox(-180, -90, 180, 90), - new Byte((byte) 4), new GeoPoint(53.11, 8.85), - null, 0, 0, 0, "de", "comment", "author", null); - - private static final String CACHE_DIRECTORY = "/Android/data/org.oscim.app/cache/"; - private static final String CACHE_FILE = "%d-%d-%d.tile"; - - private final static float REF_TILE_SIZE = 4096.0f; - - // 'open' state - private boolean mOpen = false; - private static File cacheDir; - - private final int MAX_TILE_TAGS = 100; - private Tag[] curTags = new Tag[MAX_TILE_TAGS]; - private int mCurTagCnt; - - private IMapDataSink mMapGenerator; - private float mScaleFactor; - private MapTile mTile; - - private final boolean debug = false; - private LwHttp lwHttp; - - private final UTF8Decoder mStringDecoder; - private final MapElement mElem; - +public class MapDatabase extends ProtobufMapDatabase { public MapDatabase() { - mStringDecoder = new UTF8Decoder(); - mElem = new MapElement(); + super(new TileDecoder()); + mConn = new LwHttp("application/osmtile", "osmtile", false); } - @Override - public QueryResult executeQuery(MapTile tile, IMapDataSink mapDataSink) { - QueryResult result = QueryResult.SUCCESS; + static class TileDecoder extends ProtobufDecoder { + private final static String TAG = TileDecoder.class.getName(); + private static final int TAG_TILE_NUM_TAGS = 1; + private static final int TAG_TILE_TAG_KEYS = 2; + private static final int TAG_TILE_TAG_VALUES = 3; - mTile = tile; + private static final int TAG_TILE_LINE = 11; + private static final int TAG_TILE_POLY = 12; + private static final int TAG_TILE_POINT = 13; + // private static final int TAG_TILE_LABEL = 21; + // private static final int TAG_TILE_WATER = 31; - mMapGenerator = mapDataSink; + private static final int TAG_ELEM_NUM_INDICES = 1; + private static final int TAG_ELEM_TAGS = 11; + private static final int TAG_ELEM_INDEX = 12; + private static final int TAG_ELEM_COORDS = 13; + private static final int TAG_ELEM_LAYER = 21; + private static final int TAG_ELEM_HEIGHT = 31; + private static final int TAG_ELEM_MIN_HEIGHT = 32; + private static final int TAG_ELEM_PRIORITY = 41; + + private short[] mSArray = new short[100]; + private final Tag[] mTmpTags = new Tag[20]; + private final Tag[][] mElementTags; + private final int MAX_TILE_TAGS = 100; + private Tag[] curTags = new Tag[MAX_TILE_TAGS]; + private int mCurTagCnt; // scale coordinates to tile size - mScaleFactor = REF_TILE_SIZE / Tile.SIZE; + private final static float REF_TILE_SIZE = 4096.0f; + private float mScale; - File f = null; + private Tile mTile; - if (USE_CACHE) { - f = new File(cacheDir, String.format(CACHE_FILE, - Integer.valueOf(tile.zoomLevel), - Integer.valueOf(tile.tileX), - Integer.valueOf(tile.tileY))); + private final MapElement mElem; + + private IMapDataSink mMapDataSink; + + TileDecoder() { + mElem = new MapElement(); + + // reusable tag set + Tag[][] tags = new Tag[10][]; + for (int i = 0; i < 10; i++) + tags[i] = new Tag[i + 1]; + mElementTags = tags; - if (lwHttp.cacheRead(tile, f)) - return QueryResult.SUCCESS; } - try { + @Override + public boolean decode(Tile tile, IMapDataSink sink, InputStream is, int contentLength) + throws IOException { - if (lwHttp.sendRequest(tile) && lwHttp.readHeader() >= 0) { - lwHttp.cacheBegin(tile, f); - decode(); - } else { - Log.d(TAG, tile + " Network Error"); - result = QueryResult.FAILED; + int byteCount = readUnsignedInt(is, buffer); + Log.d(TAG, tile + " contentLength:" + byteCount); + if (byteCount < 0) { + Log.d(TAG, "invalid contentLength: " + byteCount); + return false; } - } catch (SocketException ex) { - Log.d(TAG, tile + " Socket exception: " + ex.getMessage()); - result = QueryResult.FAILED; - } catch (SocketTimeoutException ex) { - Log.d(TAG, tile + " Socket Timeout exception: " + ex.getMessage()); - result = QueryResult.FAILED; - } catch (UnknownHostException ex) { - Log.d(TAG, tile + " no network"); - result = QueryResult.FAILED; - } catch (Exception ex) { - ex.printStackTrace(); - result = QueryResult.FAILED; - } - lwHttp.mLastRequest = SystemClock.elapsedRealtime(); + setInputStream(is, byteCount); - if (result == QueryResult.SUCCESS) { + mTile = tile; + mMapDataSink = sink; - lwHttp.cacheFinish(tile, f, true); - } else { - lwHttp.cacheFinish(tile, f, false); - lwHttp.close(); - } - return result; - } + mScale = REF_TILE_SIZE / Tile.SIZE; - @Override - public String getMapProjection() { - return null; - } + mCurTagCnt = 0; - @Override - public MapInfo getMapInfo() { - return mMapInfo; - } + int val; + int numTags = 0; - @Override - public boolean isOpen() { - return mOpen; - } + while (hasData() && (val = decodeVarint32()) > 0) { + // read tag and wire type + int tag = (val >> 3); - @Override - public OpenResult open(MapOptions options) { - if (mOpen) - return OpenResult.SUCCESS; + switch (tag) { + case TAG_TILE_NUM_TAGS: + numTags = decodeVarint32(); + if (numTags > curTags.length) + curTags = new Tag[numTags]; + break; - if (options == null || !options.containsKey("url")) - return new OpenResult("options missing"); + case TAG_TILE_TAG_KEYS: + int len = numTags; + if (mSArray.length < len) + mSArray = new short[len]; - lwHttp = new LwHttp(); + decodeVarintArray(numTags, mSArray); + break; - if (!lwHttp.setServer(options.get("url"))) { - return new OpenResult("invalid url: " + options.get("url")); - } + case TAG_TILE_TAG_VALUES: + // this wastes one byte, as there is no packed string... + decodeTileTags(mCurTagCnt++); + break; - if (USE_CACHE) { - if (cacheDir == null) { - String externalStorageDirectory = Environment - .getExternalStorageDirectory() - .getAbsolutePath(); - String cacheDirectoryPath = externalStorageDirectory + CACHE_DIRECTORY; - cacheDir = createDirectory(cacheDirectoryPath); - } - } + case TAG_TILE_LINE: + case TAG_TILE_POLY: + case TAG_TILE_POINT: + decodeTileElement(tag); + break; - mOpen = true; - initDecorder(); - - return OpenResult.SUCCESS; - } - - @Override - public void close() { - mOpen = false; - - lwHttp.close(); - - if (USE_CACHE) { - cacheDir = null; - } - } - - @Override - public void cancel() { - } - - private static File createDirectory(String pathName) { - File file = new File(pathName); - if (!file.exists() && !file.mkdirs()) { - throw new IllegalArgumentException("could not create directory: " + file); - } else if (!file.isDirectory()) { - throw new IllegalArgumentException("not a directory: " + file); - } else if (!file.canRead()) { - throw new IllegalArgumentException("cannot read directory: " + file); - } else if (!file.canWrite()) { - throw new IllegalArgumentException("cannot write directory: " + file); - } - return file; - } - - // /////////////// hand sewed tile protocol buffers decoder /////////////// - - private static final int TAG_TILE_NUM_TAGS = 1; - private static final int TAG_TILE_TAG_KEYS = 2; - private static final int TAG_TILE_TAG_VALUES = 3; - - private static final int TAG_TILE_LINE = 11; - private static final int TAG_TILE_POLY = 12; - private static final int TAG_TILE_POINT = 13; - // private static final int TAG_TILE_LABEL = 21; - // private static final int TAG_TILE_WATER = 31; - - private static final int TAG_ELEM_NUM_INDICES = 1; - private static final int TAG_ELEM_TAGS = 11; - private static final int TAG_ELEM_INDEX = 12; - private static final int TAG_ELEM_COORDS = 13; - private static final int TAG_ELEM_LAYER = 21; - private static final int TAG_ELEM_HEIGHT = 31; - private static final int TAG_ELEM_MIN_HEIGHT = 32; - private static final int TAG_ELEM_PRIORITY = 41; - - private short[] mTmpKeys = new short[100]; - private final Tag[] mTmpTags = new Tag[20]; - private Tag[][] mElementTags; - - private void initDecorder() { - // reusable tag set - Tag[][] tags = new Tag[10][]; - for (int i = 0; i < 10; i++) - tags[i] = new Tag[i + 1]; - mElementTags = tags; - } - - private boolean decode() throws IOException { - - mCurTagCnt = 0; - - int val; - int numTags = 0; - - while (lwHttp.hasData() && (val = decodeVarint32()) > 0) { - // read tag and wire type - int tag = (val >> 3); - - switch (tag) { - case TAG_TILE_NUM_TAGS: - numTags = decodeVarint32(); - if (numTags > curTags.length) - curTags = new Tag[numTags]; - break; - - case TAG_TILE_TAG_KEYS: - mTmpKeys = decodeShortArray(numTags, mTmpKeys); - break; - - case TAG_TILE_TAG_VALUES: - // this wastes one byte, as there is no packed string... - decodeTileTags(mCurTagCnt++); - break; - - case TAG_TILE_LINE: - case TAG_TILE_POLY: - case TAG_TILE_POINT: - decodeTileElement(tag); - break; - - default: - Log.d(TAG, mTile + " invalid type for tile: " + tag); - return false; - } - } - return true; - } - - private boolean decodeTileTags(int curTag) throws IOException { - String tagString = decodeString(); - - String key = Tags.keys[mTmpKeys[curTag]]; - Tag tag; - - if (key == Tag.TAG_KEY_NAME) - tag = new Tag(key, tagString, false); - else - tag = new Tag(key, tagString, true); - if (debug) - Log.d(TAG, mTile + " add tag: " + curTag + " " + tag); - curTags[curTag] = tag; - - return true; - } - - private int decodeWayIndices(int indexCnt) throws IOException { - mElem.index = decodeShortArray(indexCnt, mElem.index); - - short[] index = mElem.index; - int coordCnt = 0; - - for (int i = 0; i < indexCnt; i++) - coordCnt += index[i] *= 2; - - // set end marker - if (indexCnt < index.length) - index[indexCnt] = -1; - - return coordCnt; - } - - private boolean decodeTileElement(int type) throws IOException { - - int bytes = decodeVarint32(); - Tag[] tags = null; - short[] index = null; - - int end = lwHttp.position() + bytes; - int indexCnt = 1; - - boolean skip = false; - boolean fail = false; - - int coordCnt = 0; - if (type == TAG_TILE_POINT) { - coordCnt = 2; - mElem.index[0] = 2; - } - - mElem.layer = 5; - mElem.priority = 0; - mElem.height = 0; - mElem.minHeight = 0; - - while (lwHttp.position() < end) { - // read tag and wire type - int val = decodeVarint32(); - if (val == 0) - break; - - int tag = (val >> 3); - - switch (tag) { - case TAG_ELEM_TAGS: - tags = decodeElementTags(); - break; - - case TAG_ELEM_NUM_INDICES: - indexCnt = decodeVarint32(); - break; - - case TAG_ELEM_INDEX: - coordCnt = decodeWayIndices(indexCnt); - break; - - case TAG_ELEM_COORDS: - if (coordCnt == 0) { - Log.d(TAG, mTile + " no coordinates"); - skip = true; - } - int cnt = decodeWayCoordinates(skip, coordCnt); - - if (cnt != coordCnt) { - Log.d(TAG, mTile + " wrong number of coordintes"); - fail = true; - } - break; - - case TAG_ELEM_LAYER: - mElem.layer = decodeVarint32(); - break; - - case TAG_ELEM_HEIGHT: - mElem.height = decodeVarint32(); - break; - - case TAG_ELEM_MIN_HEIGHT: - mElem.minHeight = decodeVarint32(); - break; - - case TAG_ELEM_PRIORITY: - mElem.priority = decodeVarint32(); - break; - - default: - Log.d(TAG, mTile + " invalid type for way: " + tag); - } - } - - if (fail || tags == null || indexCnt == 0) { - Log.d(TAG, mTile + " failed reading way: bytes:" + bytes + " index:" - + (Arrays.toString(index)) + " tag:" - + (tags != null ? Arrays.deepToString(tags) : "null") + " " - + indexCnt + " " + coordCnt); - return false; - } - - mElem.tags = tags; - switch (type) { - case TAG_TILE_LINE: - mElem.type = GeometryType.LINE; - break; - case TAG_TILE_POLY: - mElem.type = GeometryType.POLY; - break; - case TAG_TILE_POINT: - mElem.type = GeometryType.POINT; - break; - } - - mMapGenerator.process(mElem); - - return true; - } - - private Tag[] decodeElementTags() throws IOException { - int bytes = decodeVarint32(); - - Tag[] tmp = mTmpTags; - - int cnt = 0; - int end = lwHttp.position() + bytes; - int max = mCurTagCnt; - - while (lwHttp.position() < end) { - int tagNum = decodeVarint32(); - - if (tagNum < 0) { - Log.d(TAG, "NULL TAG: " + mTile + " invalid tag:" + tagNum + " " + cnt); - } else if (tagNum < Tags.MAX) { - tmp[cnt++] = Tags.tags[tagNum]; - } else { - tagNum -= Tags.LIMIT; - - if (tagNum >= 0 && tagNum < max) { - // Log.d(TAG, "variable tag: " + curTags[tagNum]); - tmp[cnt++] = curTags[tagNum]; - } else { - Log.d(TAG, "NULL TAG: " + mTile + " could not find tag:" - + tagNum + " " + cnt); + default: + Log.d(TAG, mTile + " invalid type for tile: " + tag); + return false; } } + return true; } - if (cnt == 0) { - Log.d(TAG, "got no TAG!"); - } - Tag[] tags; + private boolean decodeTileTags(int curTag) throws IOException { + String tagString = decodeString(); - if (cnt < 11) - tags = mElementTags[cnt - 1]; - else - tags = new Tag[cnt]; + String key = Tags.keys[mSArray[curTag]]; + Tag tag; - for (int i = 0; i < cnt; i++) - tags[i] = tmp[i]; + if (key == Tag.TAG_KEY_NAME) + tag = new Tag(key, tagString, false); + else + tag = new Tag(key, tagString, true); + if (debug) + Log.d(TAG, mTile + " add tag: " + curTag + " " + tag); + curTags[curTag] = tag; - return tags; - } - - private final static int VARINT_LIMIT = 5; - private final static int VARINT_MAX = 10; - - private int decodeWayCoordinates(boolean skip, int nodes) throws IOException { - int bytes = decodeVarint32(); - - lwHttp.readBuffer(bytes); - - if (skip) { - lwHttp.bufferPos += bytes; - return nodes; + return true; } - int cnt = 0; + private int decodeWayIndices(int indexCnt) throws IOException { + mElem.ensureIndexSize(indexCnt, false); + decodeVarintArray(indexCnt, mElem.index); - int lastX = 0; - int lastY = 0; - boolean even = true; + short[] index = mElem.index; + int coordCnt = 0; - float scale = mScaleFactor; - float[] coords = mElem.ensurePointSize(nodes, false); + for (int i = 0; i < indexCnt; i++) { + coordCnt += index[i]; + index[i] *= 2; + } + // set end marker + if (indexCnt < index.length) + index[indexCnt] = -1; - byte[] buf = lwHttp.buffer; - int pos = lwHttp.bufferPos; - int end = pos + bytes; - int val; + return coordCnt; + } - while (pos < end) { - if (buf[pos] >= 0) { - val = buf[pos++]; + private boolean decodeTileElement(int type) throws IOException { - } else if (buf[pos + 1] >= 0) { - val = (buf[pos++] & 0x7f) - | buf[pos++] << 7; + int bytes = decodeVarint32(); + Tag[] tags = null; + short[] index = null; - } else if (buf[pos + 2] >= 0) { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++]) << 14; + int end = position() + bytes; + int indexCnt = 1; - } else if (buf[pos + 3] >= 0) { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++] & 0x7f) << 14 - | (buf[pos++]) << 21; + boolean fail = false; - } else { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++] & 0x7f) << 14 - | (buf[pos++] & 0x7f) << 21 - | (buf[pos]) << 28; + int coordCnt = 0; + if (type == TAG_TILE_POINT) { + coordCnt = 1; + mElem.index[0] = 2; + } - int max = pos + VARINT_LIMIT; - while (pos < max) - if (buf[pos++] >= 0) + mElem.layer = 5; + mElem.priority = 0; + mElem.height = 0; + mElem.minHeight = 0; + + while (position() < end) { + // read tag and wire type + int val = decodeVarint32(); + if (val == 0) + break; + + int tag = (val >> 3); + + switch (tag) { + case TAG_ELEM_TAGS: + tags = decodeElementTags(); break; - if (pos == max) - throw new IOException("malformed VarInt32 in " + mTile); - } - - // zigzag decoding - int s = ((val >>> 1) ^ -(val & 1)); - - if (even) { - lastX = lastX + s; - coords[cnt++] = lastX / scale; - even = false; - } else { - lastY = lastY + s; - coords[cnt++] = lastY / scale; - even = true; - } - } - - lwHttp.bufferPos = pos; - - return cnt; - } - - private short[] decodeShortArray(int num, short[] array) throws IOException { - int bytes = decodeVarint32(); - - if (array.length < num) - array = new short[num]; - - lwHttp.readBuffer(bytes); - - int cnt = 0; - - byte[] buf = lwHttp.buffer; - int pos = lwHttp.bufferPos; - int end = pos + bytes; - int val; - - while (pos < end) { - if (buf[pos] >= 0) { - val = buf[pos++]; - } else if (buf[pos + 1] >= 0) { - val = (buf[pos++] & 0x7f) - | buf[pos++] << 7; - } else if (buf[pos + 2] >= 0) { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++]) << 14; - } else if (buf[pos + 3] >= 0) { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++] & 0x7f) << 14 - | (buf[pos++]) << 21; - } else { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++] & 0x7f) << 14 - | (buf[pos++] & 0x7f) << 21 - | (buf[pos]) << 28; - - int max = pos + VARINT_LIMIT; - while (pos < max) - if (buf[pos++] >= 0) + case TAG_ELEM_NUM_INDICES: + indexCnt = decodeVarint32(); break; - if (pos == max) - throw new IOException("malformed VarInt32 in " + mTile); - } - - array[cnt++] = (short) val; - } - - lwHttp.bufferPos = pos; - - return array; - } - - private int decodeVarint32() throws IOException { - if (lwHttp.bufferPos + VARINT_MAX > lwHttp.bufferFill) - lwHttp.readBuffer(4096); - - byte[] buf = lwHttp.buffer; - int pos = lwHttp.bufferPos; - int val; - - if (buf[pos] >= 0) { - val = buf[pos++]; - } else { - - if (buf[pos + 1] >= 0) { - val = (buf[pos++] & 0x7f) - | (buf[pos++]) << 7; - - } else if (buf[pos + 2] >= 0) { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++]) << 14; - - } else if (buf[pos + 3] >= 0) { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++] & 0x7f) << 14 - | (buf[pos++]) << 21; - } else { - val = (buf[pos++] & 0x7f) - | (buf[pos++] & 0x7f) << 7 - | (buf[pos++] & 0x7f) << 14 - | (buf[pos++] & 0x7f) << 21 - | (buf[pos]) << 28; - - // 'Discard upper 32 bits' - int max = pos + VARINT_LIMIT; - while (pos < max) - if (buf[pos++] >= 0) + case TAG_ELEM_INDEX: + coordCnt = decodeWayIndices(indexCnt); break; - if (pos == max) - throw new IOException("malformed VarInt32 in " + mTile); + case TAG_ELEM_COORDS: + if (coordCnt == 0) { + Log.d(TAG, mTile + " no coordinates"); + } + + mElem.ensurePointSize(coordCnt, false); + int cnt = decodeInterleavedPoints(mElem.points, mScale); + + if (cnt != coordCnt) { + Log.d(TAG, mTile + " wrong number of coordintes"); + fail = true; + } + break; + + case TAG_ELEM_LAYER: + mElem.layer = decodeVarint32(); + break; + + case TAG_ELEM_HEIGHT: + mElem.height = decodeVarint32(); + break; + + case TAG_ELEM_MIN_HEIGHT: + mElem.minHeight = decodeVarint32(); + break; + + case TAG_ELEM_PRIORITY: + mElem.priority = decodeVarint32(); + break; + + default: + Log.d(TAG, mTile + " invalid type for way: " + tag); + } } + + if (fail || tags == null || indexCnt == 0) { + Log.d(TAG, mTile + " failed reading way: bytes:" + bytes + " index:" + + (Arrays.toString(index)) + " tag:" + + (tags != null ? Arrays.deepToString(tags) : "null") + " " + + indexCnt + " " + coordCnt); + return false; + } + + mElem.tags = tags; + switch (type) { + case TAG_TILE_LINE: + mElem.type = GeometryType.LINE; + break; + case TAG_TILE_POLY: + mElem.type = GeometryType.POLY; + break; + case TAG_TILE_POINT: + mElem.type = GeometryType.POINT; + break; + } + + mMapDataSink.process(mElem); + + return true; } - lwHttp.bufferPos = pos; + private Tag[] decodeElementTags() throws IOException { + int bytes = decodeVarint32(); - return val; - } + Tag[] tmp = mTmpTags; - private String decodeString() throws IOException { - final int size = decodeVarint32(); - lwHttp.readBuffer(size); - final String result = mStringDecoder.decode(lwHttp.buffer, lwHttp.bufferPos, size); + int cnt = 0; + int end = position() + bytes; + int max = mCurTagCnt; - lwHttp.bufferPos += size; + while (position() < end) { + int tagNum = decodeVarint32(); - return result; + if (tagNum < 0) { + Log.d(TAG, "NULL TAG: " + mTile + " invalid tag:" + tagNum + " " + cnt); + } else if (tagNum < Tags.MAX) { + tmp[cnt++] = Tags.tags[tagNum]; + } else { + tagNum -= Tags.LIMIT; + if (tagNum >= 0 && tagNum < max) { + // Log.d(TAG, "variable tag: " + curTags[tagNum]); + tmp[cnt++] = curTags[tagNum]; + } else { + Log.d(TAG, "NULL TAG: " + mTile + " could not find tag:" + + tagNum + " " + cnt); + } + } + } + + if (cnt == 0) { + Log.d(TAG, "got no TAG!"); + } + Tag[] tags; + + if (cnt < 11) + tags = mElementTags[cnt - 1]; + else + tags = new Tag[cnt]; + + for (int i = 0; i < cnt; i++) + tags[i] = tmp[i]; + + return tags; + } } } diff --git a/src/org/oscim/database/oscimap4/MapDatabase.java b/src/org/oscim/database/oscimap4/MapDatabase.java index 4f3b5cbf..32fb25e1 100644 --- a/src/org/oscim/database/oscimap4/MapDatabase.java +++ b/src/org/oscim/database/oscimap4/MapDatabase.java @@ -14,127 +14,16 @@ */ package org.oscim.database.oscimap4; -import java.io.InputStream; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.UnknownHostException; - -import org.oscim.core.BoundingBox; -import org.oscim.core.GeoPoint; -import org.oscim.database.IMapDataSink; -import org.oscim.database.IMapDatabase; -import org.oscim.database.MapInfo; -import org.oscim.database.MapOptions; import org.oscim.database.common.LwHttp; -import org.oscim.layers.tile.MapTile; - -import android.util.Log; +import org.oscim.database.common.ProtobufMapDatabase; /** - * - * + * Protocol Version in Development */ -public class MapDatabase implements IMapDatabase { - private static final String TAG = MapDatabase.class.getName(); +public class MapDatabase extends ProtobufMapDatabase { - static final boolean USE_CACHE = false; - - private static final MapInfo mMapInfo = - new MapInfo(new BoundingBox(-180, -90, 180, 90), - new Byte((byte) 4), new GeoPoint(53.11, 8.85), - null, 0, 0, 0, "de", "comment", "author", null); - // 'open' state - private boolean mOpen = false; - private LwHttp conn; - private TileDecoder mTileDecoder; - - @Override - public QueryResult executeQuery(MapTile tile, IMapDataSink sink) { - QueryResult result = QueryResult.SUCCESS; - - try { - InputStream is; - if (!conn.sendRequest(tile)) { - Log.d(TAG, tile + " Request Failed"); - result = QueryResult.FAILED; - } else if ((is = conn.readHeader()) != null) { - boolean win = mTileDecoder.decode(is, tile, sink); - if (!win) - Log.d(TAG, tile + " failed"); - } else { - Log.d(TAG, tile + " Network Error"); - result = QueryResult.FAILED; - } - } catch (SocketException e) { - Log.d(TAG, tile + " Socket exception: " + e.getMessage()); - result = QueryResult.FAILED; - } catch (SocketTimeoutException e) { - Log.d(TAG, tile + " Socket Timeout"); - result = QueryResult.FAILED; - } catch (UnknownHostException e) { - Log.d(TAG, tile + " No Network"); - result = QueryResult.FAILED; - } catch (Exception e) { - e.printStackTrace(); - result = QueryResult.FAILED; - } - - conn.requestCompleted(); - - if (result == QueryResult.SUCCESS) { - - //conn.cacheFinish(tile, f, true); - } else { - //conn.cacheFinish(tile, f, false); - conn.close(); - } - return result; - } - - @Override - public String getMapProjection() { - return null; - } - - @Override - public MapInfo getMapInfo() { - return mMapInfo; - } - - @Override - public boolean isOpen() { - return mOpen; - } - - @Override - public OpenResult open(MapOptions options) { - String extension = ".vtm"; - - if (mOpen) - return OpenResult.SUCCESS; - - if (options == null || !options.containsKey("url")) - return new OpenResult("No URL in MapOptions"); - - conn = new LwHttp(); - - if (!conn.setServer(options.get("url"), extension, false)) { - return new OpenResult("invalid url: " + options.get("url")); - } - - mOpen = true; - mTileDecoder = new TileDecoder(); - - return OpenResult.SUCCESS; - } - - @Override - public void close() { - mOpen = false; - conn.close(); - } - - @Override - public void cancel() { + public MapDatabase() { + super(new TileDecoder()); + mConn = new LwHttp("application/x-protobuf", "vtm", false); } } diff --git a/src/org/oscim/database/oscimap4/TileDecoder.java b/src/org/oscim/database/oscimap4/TileDecoder.java index b12e9f5b..b95a9f44 100644 --- a/src/org/oscim/database/oscimap4/TileDecoder.java +++ b/src/org/oscim/database/oscimap4/TileDecoder.java @@ -31,9 +31,6 @@ import android.util.Log; public class TileDecoder extends ProtobufDecoder { private final static String TAG = TileDecoder.class.getName(); - private final MapElement mElem; - private Tile mTile; - private static final int TAG_TILE_VERSION = 1; //private static final int TAG_TILE_TIMESTAMP = 2; //private static final int TAG_TILE_ISWATER = 3; @@ -62,6 +59,10 @@ public class TileDecoder extends ProtobufDecoder { //private static final int TAG_ELEM_PRIORITY = 41; private short[] mSArray = new short[100]; + + private Tile mTile; + + private final MapElement mElem; private final Tag[][] mElementTags; private final TagSet curTags = new TagSet(100); @@ -81,11 +82,12 @@ public class TileDecoder extends ProtobufDecoder { } - boolean decode(InputStream is, Tile tile, IMapDataSink sink) + @Override + public boolean decode(Tile tile, IMapDataSink sink, InputStream is, int contentLength) throws IOException { int byteCount = readUnsignedInt(is, buffer); - Log.d(TAG, tile + " contentLength:"+byteCount); + Log.d(TAG, tile + " contentLength:" + byteCount); if (byteCount < 0) { Log.d(TAG, "invalid contentLength: " + byteCount); return false; @@ -229,14 +231,15 @@ public class TileDecoder extends ProtobufDecoder { private int decodeWayIndices(int indexCnt) throws IOException { mElem.ensureIndexSize(indexCnt, false); - //mElem.index = decodeVarintArray(indexCnt, mElem.index); short[] index = mElem.index; int coordCnt = 0; - for (int i = 0; i < indexCnt; i++) - coordCnt += index[i] *= 2; + for (int i = 0; i < indexCnt; i++) { + coordCnt += index[i]; + index[i] *= 2; + } // set end marker if (indexCnt < index.length) @@ -260,7 +263,7 @@ public class TileDecoder extends ProtobufDecoder { int coordCnt = 0; if (type == TAG_TILE_POINT) { - coordCnt = 2; + coordCnt = 1; mElem.index[0] = 2; } @@ -297,14 +300,14 @@ public class TileDecoder extends ProtobufDecoder { case TAG_ELEM_COORDS: if (coordCnt == 0) { Log.d(TAG, mTile + " no coordinates"); - //skip = true; } mElem.ensurePointSize(coordCnt, false); - int cnt = decodeInterleavedPoints(mElem.points, coordCnt, mScaleFactor); + int cnt = decodeInterleavedPoints(mElem.points, mScaleFactor); if (cnt != coordCnt) { - Log.d(TAG, mTile + " wrong number of coordintes"); + Log.d(TAG, mTile + " wrong number of coordintes " + + coordCnt + "/" + cnt); fail = true; } break; diff --git a/src/org/oscim/layers/tile/vector/MapTileLayer.java b/src/org/oscim/layers/tile/vector/MapTileLayer.java index 0e0d4b9a..b34aceca 100644 --- a/src/org/oscim/layers/tile/vector/MapTileLayer.java +++ b/src/org/oscim/layers/tile/vector/MapTileLayer.java @@ -73,7 +73,6 @@ public class MapTileLayer extends TileLayer { pauseLoaders(true); - //mJobQueue.clear(); mTileManager.clearJobs(); mMapOptions = options; @@ -97,12 +96,10 @@ public class MapTileLayer extends TileLayer { mMapDatabase = mapDatabase; } - if (options.db == MapDatabases.OSCIMAP_READER || - options.db == MapDatabases.MAPSFORGE_FILE || - options.db == MapDatabases.TEST_READER) - MapView.enableClosePolygons = true; - else + if (options.db == MapDatabases.OPENSCIENCEMAP1) MapView.enableClosePolygons = false; + else + MapView.enableClosePolygons = true; mTileManager.setZoomTable(mMapDatabase.getMapInfo().zoomLevel);