reduce points on-the-fly while reading from mapfile

This commit is contained in:
Hannes Janetzek 2013-09-23 01:33:24 +02:00
parent 8a2060ca6e
commit 5c011a2402

View File

@ -18,8 +18,11 @@ package org.oscim.tiling.source.mapfile;
import java.io.IOException; import java.io.IOException;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import org.oscim.backend.Log;
import org.oscim.core.GeometryBuffer;
import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.GeometryBuffer.GeometryType;
import org.oscim.core.MapElement; import org.oscim.core.MapElement;
import org.oscim.core.MercatorProjection;
import org.oscim.core.Tag; import org.oscim.core.Tag;
import org.oscim.core.Tile; import org.oscim.core.Tile;
import org.oscim.tiling.MapTile; import org.oscim.tiling.MapTile;
@ -27,8 +30,6 @@ import org.oscim.tiling.source.ITileDataSink;
import org.oscim.tiling.source.ITileDataSource; import org.oscim.tiling.source.ITileDataSource;
import org.oscim.tiling.source.mapfile.header.SubFileParameter; import org.oscim.tiling.source.mapfile.header.SubFileParameter;
import org.oscim.backend.Log;
/** /**
* A class for reading binary map files. * A class for reading binary map files.
* *
@ -36,41 +37,27 @@ import org.oscim.backend.Log;
* href="http://code.google.com/p/mapsforge/wiki/SpecificationBinaryMapFile">Specification</a> * href="http://code.google.com/p/mapsforge/wiki/SpecificationBinaryMapFile">Specification</a>
*/ */
public class MapDatabase implements ITileDataSource { public class MapDatabase implements ITileDataSource {
/** /** Bitmask to extract the block offset from an index entry. */
* Bitmask to extract the block offset from an index entry.
*/
private static final long BITMASK_INDEX_OFFSET = 0x7FFFFFFFFFL; private static final long BITMASK_INDEX_OFFSET = 0x7FFFFFFFFFL;
/** /** Bitmask to extract the water information from an index entry. */
* Bitmask to extract the water information from an index entry.
*/
private static final long BITMASK_INDEX_WATER = 0x8000000000L; private static final long BITMASK_INDEX_WATER = 0x8000000000L;
/** /** Debug message prefix for the block signature. */
* Debug message prefix for the block signature.
*/
private static final String DEBUG_SIGNATURE_BLOCK = "block signature: "; private static final String DEBUG_SIGNATURE_BLOCK = "block signature: ";
/** /** Debug message prefix for the POI signature. */
* Debug message prefix for the POI signature.
*/
// private static final String DEBUG_SIGNATURE_POI = "POI signature: "; // private static final String DEBUG_SIGNATURE_POI = "POI signature: ";
/** /** Debug message prefix for the way signature. */
* Debug message prefix for the way signature.
*/
private static final String DEBUG_SIGNATURE_WAY = "way signature: "; private static final String DEBUG_SIGNATURE_WAY = "way signature: ";
/** /** Error message for an invalid first way offset. */
* Error message for an invalid first way offset.
*/
private static final String INVALID_FIRST_WAY_OFFSET = "invalid first way offset: "; private static final String INVALID_FIRST_WAY_OFFSET = "invalid first way offset: ";
private static final String TAG = MapDatabase.class.getName(); private static final String TAG = MapDatabase.class.getName();
/** /** Maximum way nodes sequence length which is considered as valid. */
* Maximum way nodes sequence length which is considered as valid.
*/
private static final int MAXIMUM_WAY_NODES_SEQUENCE_LENGTH = 8192; private static final int MAXIMUM_WAY_NODES_SEQUENCE_LENGTH = 8192;
/** /**
@ -79,97 +66,60 @@ public class MapDatabase implements ITileDataSource {
*/ */
private static final int MAXIMUM_ZOOM_TABLE_OBJECTS = 65536; private static final int MAXIMUM_ZOOM_TABLE_OBJECTS = 65536;
/** /** Bitmask for the optional POI feature "elevation". */
* Bitmask for the optional POI feature "elevation".
*/
private static final int POI_FEATURE_ELEVATION = 0x20; private static final int POI_FEATURE_ELEVATION = 0x20;
/** /** Bitmask for the optional POI feature "house number". */
* Bitmask for the optional POI feature "house number".
*/
private static final int POI_FEATURE_HOUSE_NUMBER = 0x40; private static final int POI_FEATURE_HOUSE_NUMBER = 0x40;
/** /** Bitmask for the optional POI feature "name". */
* Bitmask for the optional POI feature "name".
*/
private static final int POI_FEATURE_NAME = 0x80; private static final int POI_FEATURE_NAME = 0x80;
/** /** Bitmask for the POI layer. */
* Bitmask for the POI layer.
*/
private static final int POI_LAYER_BITMASK = 0xf0; private static final int POI_LAYER_BITMASK = 0xf0;
/** /** Bit shift for calculating the POI layer. */
* Bit shift for calculating the POI layer.
*/
private static final int POI_LAYER_SHIFT = 4; private static final int POI_LAYER_SHIFT = 4;
/** /** Bitmask for the number of POI tags. */
* Bitmask for the number of POI tags.
*/
private static final int POI_NUMBER_OF_TAGS_BITMASK = 0x0f; private static final int POI_NUMBER_OF_TAGS_BITMASK = 0x0f;
/** /** Length of the debug signature at the beginning of each block. */
* Length of the debug signature at the beginning of each block.
*/
private static final byte SIGNATURE_LENGTH_BLOCK = 32; private static final byte SIGNATURE_LENGTH_BLOCK = 32;
/** /** Length of the debug signature at the beginning of each POI. */
* Length of the debug signature at the beginning of each POI.
*/
private static final byte SIGNATURE_LENGTH_POI = 32; private static final byte SIGNATURE_LENGTH_POI = 32;
/** /** Length of the debug signature at the beginning of each way. */
* Length of the debug signature at the beginning of each way.
*/
private static final byte SIGNATURE_LENGTH_WAY = 32; private static final byte SIGNATURE_LENGTH_WAY = 32;
/** /** Bitmask for the optional way data blocks byte. */
* Bitmask for the optional way data blocks byte.
*/
private static final int WAY_FEATURE_DATA_BLOCKS_BYTE = 0x08; private static final int WAY_FEATURE_DATA_BLOCKS_BYTE = 0x08;
/** /** Bitmask for the optional way double delta encoding. */
* Bitmask for the optional way double delta encoding.
*/
private static final int WAY_FEATURE_DOUBLE_DELTA_ENCODING = 0x04; private static final int WAY_FEATURE_DOUBLE_DELTA_ENCODING = 0x04;
/** /** Bitmask for the optional way feature "house number". */
* Bitmask for the optional way feature "house number".
*/
private static final int WAY_FEATURE_HOUSE_NUMBER = 0x40; private static final int WAY_FEATURE_HOUSE_NUMBER = 0x40;
/** /** Bitmask for the optional way feature "label position". */
* Bitmask for the optional way feature "label position".
*/
private static final int WAY_FEATURE_LABEL_POSITION = 0x10; private static final int WAY_FEATURE_LABEL_POSITION = 0x10;
/** /** Bitmask for the optional way feature "name". */
* Bitmask for the optional way feature "name".
*/
private static final int WAY_FEATURE_NAME = 0x80; private static final int WAY_FEATURE_NAME = 0x80;
/** /** Bitmask for the optional way feature "reference". */
* Bitmask for the optional way feature "reference".
*/
private static final int WAY_FEATURE_REF = 0x20; private static final int WAY_FEATURE_REF = 0x20;
/** /** Bitmask for the way layer. */
* Bitmask for the way layer.
*/
private static final int WAY_LAYER_BITMASK = 0xf0; private static final int WAY_LAYER_BITMASK = 0xf0;
/** /** Bit shift for calculating the way layer. */
* Bit shift for calculating the way layer.
*/
private static final int WAY_LAYER_SHIFT = 4; private static final int WAY_LAYER_SHIFT = 4;
/** /** Bitmask for the number of way tags. */
* Bitmask for the number of way tags.
*/
private static final int WAY_NUMBER_OF_TAGS_BITMASK = 0x0f; private static final int WAY_NUMBER_OF_TAGS_BITMASK = 0x0f;
private long mFileSize; private long mFileSize;
private boolean mDebugFile; private boolean mDebugFile;
private RandomAccessFile mInputFile; private RandomAccessFile mInputFile;
@ -188,6 +138,8 @@ public class MapDatabase implements ITileDataSource {
private final MapFileTileSource mTileSource; private final MapFileTileSource mTileSource;
//private int mReductionCnt;
//private int mSkipPoly;
@Override @Override
public QueryResult executeQuery(MapTile tile, ITileDataSink mapDataSink) { public QueryResult executeQuery(MapTile tile, ITileDataSink mapDataSink) {
@ -201,15 +153,38 @@ public class MapDatabase implements ITileDataSource {
try { try {
mTile = tile; mTile = tile;
// size of tile in map coordinates;
double size = 1.0 / (1 << tile.zoomLevel);
// simplification tolerance
int pixel = (tile.zoomLevel > 11) ? 1 : 2;
int simplify = Tile.SIZE / pixel;
// translate screen pixel for tile to latitude and longitude
// tolerance for point reduction before projection.
minLat = (int) (Math.abs(MercatorProjection.toLatitude(tile.y + size)
- MercatorProjection.toLatitude(tile.y)) * 1e6) / simplify;
minLon = (int) (Math.abs(MercatorProjection.toLongitude(tile.x + size)
- MercatorProjection.toLongitude(tile.x)) * 1e6) / simplify;
//mReductionCnt = 0;
//mSkipPoly = 0;
//Log.d(TAG, "simplify by " + minLat + "/" + minLon);
QueryParameters queryParameters = new QueryParameters(); QueryParameters queryParameters = new QueryParameters();
queryParameters.queryZoomLevel = mTileSource.fileHeader queryParameters.queryZoomLevel =
.getQueryZoomLevel(tile.zoomLevel); mTileSource.fileHeader.getQueryZoomLevel(tile.zoomLevel);
// get and check the sub-file for the query zoom level // get and check the sub-file for the query zoom level
SubFileParameter subFileParameter = mTileSource.fileHeader SubFileParameter subFileParameter =
.getSubFileParameter(queryParameters.queryZoomLevel); mTileSource.fileHeader.getSubFileParameter(queryParameters.queryZoomLevel);
if (subFileParameter == null) { if (subFileParameter == null) {
Log.w(TAG, "no sub-file for zoom level: " Log.w(TAG, "no sub-file for zoom level: "
+ queryParameters.queryZoomLevel); + queryParameters.queryZoomLevel);
return QueryResult.FAILED; return QueryResult.FAILED;
} }
@ -220,6 +195,9 @@ public class MapDatabase implements ITileDataSource {
Log.e(TAG, e.getMessage()); Log.e(TAG, e.getMessage());
return QueryResult.FAILED; return QueryResult.FAILED;
} }
//Log.d(TAG, "reduced points " + mReductionCnt + " / polys " + mSkipPoly);
return QueryResult.SUCCESS; return QueryResult.SUCCESS;
} }
@ -251,7 +229,6 @@ public class MapDatabase implements ITileDataSource {
Log.e(TAG, e.getMessage()); Log.e(TAG, e.getMessage());
} }
} }
} }
/** /**
@ -346,8 +323,8 @@ public class MapDatabase implements ITileDataSource {
long blockNumber = row * subFileParameter.blocksWidth + column; long blockNumber = row * subFileParameter.blocksWidth + column;
// get the current index entry // get the current index entry
long currentBlockIndexEntry = mTileSource.databaseIndexCache.getIndexEntry( long currentBlockIndexEntry =
subFileParameter, blockNumber); mTileSource.databaseIndexCache.getIndexEntry(subFileParameter, blockNumber);
// check if the current query would still return a water tile // check if the current query would still return a water tile
if (queryIsWater) { if (queryIsWater) {
@ -373,8 +350,9 @@ public class MapDatabase implements ITileDataSource {
nextBlockPointer = subFileParameter.subFileSize; nextBlockPointer = subFileParameter.subFileSize;
} else { } else {
// get and check the next block pointer // get and check the next block pointer
nextBlockPointer = mTileSource.databaseIndexCache.getIndexEntry( nextBlockPointer =
subFileParameter, blockNumber + 1) mTileSource.databaseIndexCache.getIndexEntry(subFileParameter,
blockNumber + 1)
& BITMASK_INDEX_OFFSET; & BITMASK_INDEX_OFFSET;
if (nextBlockPointer < 1 if (nextBlockPointer < 1
|| nextBlockPointer > subFileParameter.subFileSize) { || nextBlockPointer > subFileParameter.subFileSize) {
@ -415,14 +393,15 @@ public class MapDatabase implements ITileDataSource {
} }
// calculate the top-left coordinates of the underlying tile // calculate the top-left coordinates of the underlying tile
double tileLatitudeDeg = Projection.tileYToLatitude( double tileLatitudeDeg =
subFileParameter.boundaryTileTop + row, Projection.tileYToLatitude(subFileParameter.boundaryTileTop + row,
subFileParameter.baseZoomLevel); subFileParameter.baseZoomLevel);
double tileLongitudeDeg = Projection.tileXToLongitude( double tileLongitudeDeg =
subFileParameter.boundaryTileLeft Projection.tileXToLongitude(subFileParameter.boundaryTileLeft + column,
+ column, subFileParameter.baseZoomLevel); subFileParameter.baseZoomLevel);
mTileLatitude = (int) (tileLatitudeDeg * 1000000);
mTileLongitude = (int) (tileLongitudeDeg * 1000000); mTileLatitude = (int) (tileLatitudeDeg * 1E6);
mTileLongitude = (int) (tileLongitudeDeg * 1E6);
processBlock(queryParameters, subFileParameter, mapDataSink); processBlock(queryParameters, subFileParameter, mapDataSink);
} }
@ -438,7 +417,6 @@ public class MapDatabase implements ITileDataSource {
// mWayNodePosition += 8; // mWayNodePosition += 8;
// mapDatabaseCallback.renderWaterBackground(tags, wayDataContainer); // mapDatabaseCallback.renderWaterBackground(tags, wayDataContainer);
// } // }
} }
/** /**
@ -543,15 +521,16 @@ public class MapDatabase implements ITileDataSource {
// Integer.toString(mReadBuffer.readSignedInt()))); // Integer.toString(mReadBuffer.readSignedInt())));
} }
longitude = (int) (longitude / divx - dx); float lon = (float) (longitude / divx - dx);
double sinLat = Math.sin(latitude * PI180); double sinLat = Math.sin(latitude * PI180);
latitude = (int) (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy); float lat = Tile.SIZE
- (float) ((Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy));
mElem.clear(); mElem.clear();
mElem.startPoints();
mElem.addPoint(longitude, latitude);
mElem.type = GeometryType.POINT;
mElem.setLayer(layer); mElem.setLayer(layer);
mElem.startPoints();
mElem.addPoint(lon, lat);
mapDataSink.process(mElem); mapDataSink.process(mElem);
} }
@ -572,7 +551,7 @@ public class MapDatabase implements ITileDataSource {
if (wayLengths.length > numBlocks) if (wayLengths.length > numBlocks)
wayLengths[numBlocks] = -1; wayLengths[numBlocks] = -1;
mElem.pointPos = 0; //mElem.pointPos = 0;
// read the way coordinate blocks // read the way coordinate blocks
for (int coordinateBlock = 0; coordinateBlock < numBlocks; ++coordinateBlock) { for (int coordinateBlock = 0; coordinateBlock < numBlocks; ++coordinateBlock) {
@ -605,44 +584,53 @@ public class MapDatabase implements ITileDataSource {
mReadBuffer.readSignedInt(buffer, length); mReadBuffer.readSignedInt(buffer, length);
float[] outBuffer = mElem.ensurePointSize(mElem.pointPos + length, true); float[] outBuffer = mElem.ensurePointSize(mElem.pointPos + length, true);
int pointPos = mElem.pointPos; int outPos = mElem.pointPos;
int lat, lon;
// get the first way node latitude offset (VBE-S) // get the first way node latitude offset
int wayNodeLatitude = mTileLatitude + buffer[0]; int firstLat = lat = mTileLatitude + buffer[0];
// get the first way node longitude offset (VBE-S) // get the first way node longitude offset
int wayNodeLongitude = mTileLongitude + buffer[1]; int firstLon = lon = mTileLongitude + buffer[1];
// store the first way node outBuffer[outPos++] = lon;
outBuffer[pointPos++] = wayNodeLongitude; outBuffer[outPos++] = lat;
outBuffer[pointPos++] = wayNodeLatitude; int cnt = 2;
int singleDeltaLatitude = 0; int deltaLat = 0;
int singleDeltaLongitude = 0; int deltaLon = 0;
int cnt = 2, nLon, nLat, dLat, dLon;
for (int pos = 2; pos < length; pos += 2) { for (int pos = 2; pos < length; pos += 2) {
deltaLat = buffer[pos] + deltaLat;
lat += deltaLat;
singleDeltaLatitude = buffer[pos] + singleDeltaLatitude; deltaLon = buffer[pos + 1] + deltaLon;
nLat = wayNodeLatitude + singleDeltaLatitude; lon += deltaLon;
dLat = nLat - wayNodeLatitude;
wayNodeLatitude = nLat;
singleDeltaLongitude = buffer[pos + 1] + singleDeltaLongitude; if (pos == length - 2) {
nLon = wayNodeLongitude + singleDeltaLongitude; boolean line = (lon != firstLon && lat != firstLat);
dLon = nLon - wayNodeLongitude; // this also removes closed ways that are not polygon,
wayNodeLongitude = nLon; // but how do we know?
if (line) {
outBuffer[outPos++] = lon;
outBuffer[outPos++] = lat;
cnt += 2;
}
if (dLon > minLon || dLon < -minLon || dLat > minLat || dLat < -minLat if (mElem.type == GeometryType.NONE) {
|| (pos == length - 2)) { mElem.type = line ? GeometryType.LINE : GeometryType.POLY;
outBuffer[pointPos++] = nLon; }
outBuffer[pointPos++] = nLat;
} else if (deltaLon > minLon || deltaLon < -minLon
|| deltaLat > minLat || deltaLat < -minLat) {
outBuffer[outPos++] = lon;
outBuffer[outPos++] = lat;
cnt += 2; cnt += 2;
} }
} }
mElem.pointPos = pointPos; //mReductionCnt += length - cnt;
mElem.pointPos = outPos;
return cnt; return cnt;
} }
@ -652,39 +640,49 @@ public class MapDatabase implements ITileDataSource {
mReadBuffer.readSignedInt(buffer, length); mReadBuffer.readSignedInt(buffer, length);
float[] outBuffer = mElem.ensurePointSize(mElem.pointPos + length, true); float[] outBuffer = mElem.ensurePointSize(mElem.pointPos + length, true);
int pointPos = mElem.pointPos; int outPos = mElem.pointPos;
int lat, lon;
// get the first way node latitude single-delta offset (VBE-S) // get the first way node latitude single-delta offset
int wayNodeLatitude = mTileLatitude + buffer[0]; int firstLat = lat = mTileLatitude + buffer[0];
// get the first way node longitude single-delta offset (VBE-S) // get the first way node longitude single-delta offset
int wayNodeLongitude = mTileLongitude + buffer[1]; int firstLon = lon = mTileLongitude + buffer[1];
// store the first way node outBuffer[outPos++] = lon;
outBuffer[pointPos++] = wayNodeLongitude; outBuffer[outPos++] = lat;
outBuffer[pointPos++] = wayNodeLatitude; int cnt = 2;
int cnt = 2, nLon, nLat, dLat, dLon;
for (int pos = 2; pos < length; pos += 2) { for (int pos = 2; pos < length; pos += 2) {
int deltaLat = buffer[pos];
lat += deltaLat;
nLat = wayNodeLatitude + buffer[pos]; int deltaLon = buffer[pos + 1];
dLat = nLat - wayNodeLatitude; lon += deltaLon;
wayNodeLatitude = nLat;
nLon = wayNodeLongitude + buffer[pos + 1]; if (pos == length - 2) {
dLon = nLon - wayNodeLongitude; boolean line = (lon != firstLon && lat != firstLat);
wayNodeLongitude = nLon;
if (dLon > minLon || dLon < -minLon || dLat > minLat || dLat < -minLat if (line) {
|| (pos == length - 2)) { outBuffer[outPos++] = lon;
outBuffer[pointPos++] = nLon; outBuffer[outPos++] = lat;
outBuffer[pointPos++] = nLat; cnt += 2;
}
if (mElem.type == GeometryType.NONE)
mElem.type = line ? GeometryType.LINE : GeometryType.POLY;
} else if (deltaLon > minLon || deltaLon < -minLon
|| deltaLat > minLat || deltaLat < -minLat) {
outBuffer[outPos++] = lon;
outBuffer[outPos++] = lat;
cnt += 2; cnt += 2;
} }
} }
mElem.pointPos = pointPos; //mReductionCnt += length - cnt;
mElem.pointPos = outPos;
return cnt; return cnt;
} }
@ -746,7 +744,8 @@ public class MapDatabase implements ITileDataSource {
int pos = mReadBuffer.getBufferPosition(); int pos = mReadBuffer.getBufferPosition();
mReadBuffer.setBufferPosition(mReadBuffer.lastTagPosition); mReadBuffer.setBufferPosition(mReadBuffer.lastTagPosition);
byte numberOfTags = (byte) (mReadBuffer.readByte() & WAY_NUMBER_OF_TAGS_BITMASK); byte numberOfTags =
(byte) (mReadBuffer.readByte() & WAY_NUMBER_OF_TAGS_BITMASK);
if (!mReadBuffer.readTags(mElem.tags, wayTags, numberOfTags)) if (!mReadBuffer.readTags(mElem.tags, wayTags, numberOfTags))
return false; return false;
@ -789,7 +788,8 @@ public class MapDatabase implements ITileDataSource {
byte featureByte = mReadBuffer.readByte(); byte featureByte = mReadBuffer.readByte();
// bit 1-6 enable optional features // bit 1-6 enable optional features
boolean featureWayDoubleDeltaEncoding = (featureByte & WAY_FEATURE_DOUBLE_DELTA_ENCODING) != 0; boolean featureWayDoubleDeltaEncoding =
(featureByte & WAY_FEATURE_DOUBLE_DELTA_ENCODING) != 0;
boolean hasName = (featureByte & WAY_FEATURE_NAME) != 0; boolean hasName = (featureByte & WAY_FEATURE_NAME) != 0;
boolean hasHouseNr = (featureByte & WAY_FEATURE_HOUSE_NUMBER) != 0; boolean hasHouseNr = (featureByte & WAY_FEATURE_HOUSE_NUMBER) != 0;
@ -843,21 +843,20 @@ public class MapDatabase implements ITileDataSource {
wayDataBlocks = 1; wayDataBlocks = 1;
} }
for (int wayDataBlock = 0; wayDataBlock < wayDataBlocks; ++wayDataBlock) { for (int wayDataBlock = 0; wayDataBlock < wayDataBlocks; wayDataBlock++) {
mElem.clear();
if (!processWayDataBlock(featureWayDoubleDeltaEncoding)) if (!processWayDataBlock(featureWayDoubleDeltaEncoding))
return false; return false;
// wayDataContainer.textPos = textPos; if (mElem.isPoly() && mElem.index[0] < 6) {
int l = mElem.index[0]; //mSkipPoly++;
continue;
}
boolean closed = mElem.points[0] == mElem.points[l - 2] projectToTile(mElem);
&& mElem.points[1] == mElem.points[l - 1];
projectToTile(mElem.points, mElem.index);
mElem.type = closed ? GeometryType.POLY : GeometryType.LINE;
mElem.setLayer(layer); mElem.setLayer(layer);
mapDataSink.process(mElem); mapDataSink.process(mElem);
} }
} }
@ -916,7 +915,10 @@ public class MapDatabase implements ITileDataSource {
private static final double PI180 = (Math.PI / 180) / 1000000.0; private static final double PI180 = (Math.PI / 180) / 1000000.0;
private static final double PIx4 = Math.PI * 4; private static final double PIx4 = Math.PI * 4;
private boolean projectToTile(float[] coords, short[] indices) { private boolean projectToTile(GeometryBuffer geom) {
float[] coords = geom.points;
short[] indices = geom.index;
long x = mTile.tileX * Tile.SIZE; long x = mTile.tileX * Tile.SIZE;
long y = mTile.tileY * Tile.SIZE + Tile.SIZE; long y = mTile.tileY * Tile.SIZE + Tile.SIZE;
@ -948,15 +950,18 @@ public class MapDatabase implements ITileDataSource {
if (cnt != 0) { if (cnt != 0) {
// drop small distance intermediate nodes // drop small distance intermediate nodes
if (lat == prevLat && lon == prevLon) if (lat == prevLat && lon == prevLon) {
//Log.d(TAG, "drop zero delta ");
continue; continue;
} }
}
coords[outPos++] = prevLon = lon; coords[outPos++] = prevLon = lon;
coords[outPos++] = prevLat = lat; coords[outPos++] = prevLat = lat;
cnt += 2; cnt += 2;
} }
if (coords[first] == coords[outPos - 2] && coords[first + 1] == coords[outPos - 1]) {
if (coords[first] == prevLon && coords[first + 1] == prevLat) {
//Log.d(TAG, "drop closed"); //Log.d(TAG, "drop closed");
indices[i] = (short) (cnt - 2); indices[i] = (short) (cnt - 2);
outPos -= 2; outPos -= 2;