diff --git a/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java b/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java index ed7c5280..9eb86d6b 100644 --- a/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java +++ b/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java @@ -1,6 +1,6 @@ /* * Copyright 2010, 2011, 2012 mapsforge.org - * Copyright 2013 Hannes Janetzek + * Copyright 2013, 2014 Hannes Janetzek * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * @@ -23,7 +23,6 @@ import static org.oscim.tiling.ITileDataSink.QueryResult.SUCCESS; import java.io.IOException; import java.io.RandomAccessFile; -import org.oscim.core.GeometryBuffer; import org.oscim.core.GeometryBuffer.GeometryType; import org.oscim.core.MapElement; import org.oscim.core.MercatorProjection; @@ -140,11 +139,33 @@ public class MapDatabase implements ITileDataSource { private final MapElement mElem = new MapElement(); - private int minLat, minLon; - private Tile mTile; + private int minDeltaLat, minDeltaLon; + + private final TileProjection mTileProjection; + private final TileClipper mTileClipper; private final MapFileTileSource mTileSource; + public MapDatabase(MapFileTileSource tileSource) throws IOException { + mTileSource = tileSource; + try { + /* open the file in read only mode */ + mInputFile = new RandomAccessFile(tileSource.mapFile, "r"); + mFileSize = mInputFile.length(); + mReadBuffer = new ReadBuffer(mInputFile); + + } catch (IOException e) { + log.error(e.getMessage()); + /* make sure that the file is closed */ + dispose(); + throw new IOException(); + } + + mTileProjection = new TileProjection(); + + mTileClipper = new TileClipper(-2, -2, Tile.SIZE + 2, Tile.SIZE + 2); + } + @Override public void query(MapTile tile, ITileDataSink sink) { @@ -157,28 +178,29 @@ public class MapDatabase implements ITileDataSource { mIntBuffer = new int[MAXIMUM_WAY_NODES_SEQUENCE_LENGTH * 2]; try { - mTile = tile; + mTileProjection.setTile(tile); + //mTile = tile; - // size of tile in map coordinates; + /* size of tile in map coordinates; */ double size = 1.0 / (1 << tile.zoomLevel); - // simplification tolerance + /* 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) + /* translate screen pixel for tile to latitude and longitude + * tolerance for point reduction before projection. */ + minDeltaLat = (int) (Math.abs(MercatorProjection.toLatitude(tile.y + size) - MercatorProjection.toLatitude(tile.y)) * 1e6) / simplify; - minLon = (int) (Math.abs(MercatorProjection.toLongitude(tile.x + size) + minDeltaLon = (int) (Math.abs(MercatorProjection.toLongitude(tile.x + size) - MercatorProjection.toLongitude(tile.x)) * 1e6) / simplify; QueryParameters queryParameters = new QueryParameters(); queryParameters.queryZoomLevel = 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.getSubFileParameter(queryParameters.queryZoomLevel); @@ -202,22 +224,6 @@ public class MapDatabase implements ITileDataSource { sink.completed(SUCCESS); } - public MapDatabase(MapFileTileSource tileSource) throws IOException { - mTileSource = tileSource; - try { - // open the file in read only mode - mInputFile = new RandomAccessFile(tileSource.mapFile, "r"); - mFileSize = mInputFile.length(); - mReadBuffer = new ReadBuffer(mInputFile); - - } catch (IOException e) { - log.error(e.getMessage()); - // make sure that the file is closed - dispose(); - throw new IOException(); - } - } - @Override public void dispose() { mReadBuffer = null; @@ -272,7 +278,7 @@ public class MapDatabase implements ITileDataSource { int poisOnQueryZoomLevel = zoomTable[zoomTableRow][0]; int waysOnQueryZoomLevel = zoomTable[zoomTableRow][1]; - // get the relative offset to the first stored way in the block + /* get the relative offset to the first stored way in the block */ int firstWayOffset = mReadBuffer.readUnsignedInt(); if (firstWayOffset < 0) { log.warn(INVALID_FIRST_WAY_OFFSET + firstWayOffset); @@ -282,7 +288,7 @@ public class MapDatabase implements ITileDataSource { return; } - // add the current buffer position to the relative first way offset + /* add the current buffer position to the relative first way offset */ firstWayOffset += mReadBuffer.getBufferPosition(); if (firstWayOffset > mReadBuffer.getBufferSize()) { log.warn(INVALID_FIRST_WAY_OFFSET + firstWayOffset); @@ -296,7 +302,7 @@ public class MapDatabase implements ITileDataSource { return; } - // finished reading POIs, check if the current buffer position is valid + /* finished reading POIs, check if the current buffer position is valid */ if (mReadBuffer.getBufferPosition() > firstWayOffset) { log.warn("invalid buffer position: " + mReadBuffer.getBufferPosition()); if (mDebugFile) { @@ -305,7 +311,7 @@ public class MapDatabase implements ITileDataSource { return; } - // move the pointer to the first way + /* move the pointer to the first way */ mReadBuffer.setBufferPosition(firstWayOffset); if (!processWays(queryParameters, mapDataSink, waysOnQueryZoomLevel)) { @@ -323,21 +329,21 @@ public class MapDatabase implements ITileDataSource { boolean queryIsWater = true; // boolean queryReadWaterInfo = false; - // read and process all blocks from top to bottom and from left to right + /* read and process all blocks from top to bottom and from left to right */ for (long row = queryParameters.fromBlockY; row <= queryParameters.toBlockY; row++) { for (long column = queryParameters.fromBlockX; column <= queryParameters.toBlockX; column++) { mCurrentCol = column - queryParameters.fromBlockX; mCurrentRow = row - queryParameters.fromBlockY; - // calculate the actual block number of the needed block in the - // file + /* calculate the actual block number of the needed block in the + * file */ long blockNumber = row * subFileParameter.blocksWidth + column; - // get the current index entry + /* get the current index entry */ long currentBlockIndexEntry = 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) { // check the water flag of the current block in its index // entry @@ -345,7 +351,7 @@ public class MapDatabase implements ITileDataSource { // queryReadWaterInfo = true; } - // get and check the current block pointer + /* get and check the current block pointer */ long currentBlockPointer = currentBlockIndexEntry & BITMASK_INDEX_OFFSET; if (currentBlockPointer < 1 || currentBlockPointer > subFileParameter.subFileSize) { @@ -355,12 +361,12 @@ public class MapDatabase implements ITileDataSource { } long nextBlockPointer; - // check if the current block is the last block in the file + /* check if the current block is the last block in the file */ if (blockNumber + 1 == subFileParameter.numberOfBlocks) { - // set the next block pointer to the end of the file + /* set the next block pointer to the end of the file */ nextBlockPointer = subFileParameter.subFileSize; } else { - // get and check the next block pointer + /* get and check the next block pointer */ nextBlockPointer = mTileSource.databaseIndexCache.getIndexEntry(subFileParameter, blockNumber + 1) @@ -373,18 +379,18 @@ public class MapDatabase implements ITileDataSource { } } - // calculate the size of the current block + /* calculate the size of the current block */ int currentBlockSize = (int) (nextBlockPointer - currentBlockPointer); if (currentBlockSize < 0) { log.warn("current block size must not be negative: " + currentBlockSize); return; } else if (currentBlockSize == 0) { - // the current block is empty, continue with the next block + /* the current block is empty, continue with the next block */ continue; } else if (currentBlockSize > ReadBuffer.MAXIMUM_BUFFER_SIZE) { - // the current block is too large, continue with the next - // block + /* the current block is too large, continue with the next + * block */ log.warn("current block size too large: " + currentBlockSize); continue; } else if (currentBlockPointer + currentBlockSize > mFileSize) { @@ -393,17 +399,17 @@ public class MapDatabase implements ITileDataSource { return; } - // seek to the current block in the map file + /* seek to the current block in the map file */ mInputFile.seek(subFileParameter.startAddress + currentBlockPointer); - // read the current block into the buffer + /* read the current block into the buffer */ if (!mReadBuffer.readFromFile(currentBlockSize)) { - // skip the current block + /* skip the current block */ log.warn("reading current block has failed: " + currentBlockSize); return; } - // calculate the top-left coordinates of the underlying tile + /* calculate the top-left coordinates of the underlying tile */ double tileLatitudeDeg = Projection.tileYToLatitude(subFileParameter.boundaryTileTop + row, subFileParameter.baseZoomLevel); @@ -438,7 +444,7 @@ public class MapDatabase implements ITileDataSource { */ private boolean processBlockSignature() { if (mDebugFile) { - // get and check the block signature + /* get and check the block signature */ mSignatureBlock = mReadBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_BLOCK); if (!mSignatureBlock.startsWith("###TileStart")) { log.warn("invalid block signature: " + mSignatureBlock); @@ -460,21 +466,13 @@ public class MapDatabase implements ITileDataSource { */ private boolean processPOIs(ITileDataSink mapDataSink, int numberOfPois) { Tag[] poiTags = mTileSource.fileInfo.poiTags; + MapElement e = mElem; + int numTags = 0; - long x = mTile.tileX * Tile.SIZE; - long y = mTile.tileY * Tile.SIZE + Tile.SIZE; - long z = Tile.SIZE << mTile.zoomLevel; - - long dx = (x - (z >> 1)); - long dy = (y - (z >> 1)); - - double divx = 180000000.0 / (z >> 1); - double divy = z / PIx4; - for (int elementCounter = numberOfPois; elementCounter != 0; --elementCounter) { if (mDebugFile) { - // get and check the POI signature + /* get and check the POI signature */ mSignaturePoi = mReadBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_POI); if (!mSignaturePoi.startsWith("***POIStart")) { log.warn("invalid POI signature: " + mSignaturePoi); @@ -483,88 +481,77 @@ public class MapDatabase implements ITileDataSource { } } - // get the POI latitude offset (VBE-S) + /* get the POI latitude offset (VBE-S) */ int latitude = mTileLatitude + mReadBuffer.readSignedInt(); - - // get the POI longitude offset (VBE-S) + /* get the POI longitude offset (VBE-S) */ int longitude = mTileLongitude + mReadBuffer.readSignedInt(); - // get the special byte which encodes multiple flags + /* get the special byte which encodes multiple flags */ byte specialByte = mReadBuffer.readByte(); - // bit 1-4 represent the layer + /* bit 1-4 represent the layer */ byte layer = (byte) ((specialByte & POI_LAYER_BITMASK) >>> POI_LAYER_SHIFT); - // bit 5-8 represent the number of tag IDs + + /* bit 5-8 represent the number of tag IDs */ byte numberOfTags = (byte) (specialByte & POI_NUMBER_OF_TAGS_BITMASK); if (numberOfTags != 0) { - if (!mReadBuffer.readTags(mElem.tags, poiTags, numberOfTags)) + if (!mReadBuffer.readTags(e.tags, poiTags, numberOfTags)) return false; numTags = numberOfTags; } - // reset to common tag position - mElem.tags.numTags = numTags; + /* reset to common tag position */ + e.tags.numTags = numTags; - // get the feature bitmask (1 byte) + /* get the feature bitmask (1 byte) */ byte featureByte = mReadBuffer.readByte(); - // bit 1-3 enable optional features - // check if the POI has a name + /* bit 1-3 enable optional features + * check if the POI has a name */ if ((featureByte & POI_FEATURE_NAME) != 0) { String str = mReadBuffer.readUTF8EncodedString(); - mElem.tags.add(new Tag(Tag.KEY_NAME, str, false)); + e.tags.add(new Tag(Tag.KEY_NAME, str, false)); } - // check if the POI has a house number + /* check if the POI has a house number */ if ((featureByte & POI_FEATURE_HOUSE_NUMBER) != 0) { // mReadBuffer.getPositionAndSkip(); // String str = mReadBuffer.readUTF8EncodedString(); } - // check if the POI has an elevation + /* check if the POI has an elevation */ if ((featureByte & POI_FEATURE_ELEVATION) != 0) { mReadBuffer.readSignedInt(); // mReadBuffer.getPositionAndSkip();// tags.add(new // Tag(Tag.TAG_KEY_ELE, // Integer.toString(mReadBuffer.readSignedInt()))); } + mTileProjection.projectPoint(latitude, longitude, e); - float lon = (float) (longitude / divx - dx); + e.setLayer(layer); - double sinLat = Math.sin(latitude * PI180); - float lat = Tile.SIZE - - (float) ((Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy)); - - mElem.clear(); - mElem.setLayer(layer); - mElem.startPoints(); - mElem.addPoint(lon, lat); - - mapDataSink.process(mElem); + mapDataSink.process(e); } return true; } - private boolean processWayDataBlock(boolean doubleDeltaEncoding) { - // get and check the number of way coordinate blocks (VBE-U) + private boolean processWayDataBlock(MapElement e, boolean doubleDeltaEncoding, boolean isLine) { + /* get and check the number of way coordinate blocks (VBE-U) */ int numBlocks = mReadBuffer.readUnsignedInt(); if (numBlocks < 1 || numBlocks > Short.MAX_VALUE) { log.warn("invalid number of way coordinate blocks: " + numBlocks); return false; } - //short[] wayLengths = new short[numBlocks]; - int[] wayLengths = mElem.ensureIndexSize(numBlocks, false); + int[] wayLengths = e.ensureIndexSize(numBlocks, false); if (wayLengths.length > numBlocks) wayLengths[numBlocks] = -1; - //mElem.pointPos = 0; - - // read the way coordinate blocks + /* read the way coordinate blocks */ for (int coordinateBlock = 0; coordinateBlock < numBlocks; ++coordinateBlock) { // get and check the number of way nodes (VBE-U) int numWayNodes = mReadBuffer.readUnsignedInt(); @@ -575,13 +562,13 @@ public class MapDatabase implements ITileDataSource { return false; } - // each way node consists of latitude and longitude + /* each way node consists of latitude and longitude */ int len = numWayNodes * 2; if (doubleDeltaEncoding) { - len = decodeWayNodesDoubleDelta(len); + len = decodeWayNodesDoubleDelta(e, len, isLine); } else { - len = decodeWayNodesSingleDelta(len); + len = decodeWayNodesSingleDelta(e, len, isLine); } wayLengths[coordinateBlock] = (short) len; } @@ -589,19 +576,19 @@ public class MapDatabase implements ITileDataSource { return true; } - private int decodeWayNodesDoubleDelta(int length) { + private int decodeWayNodesDoubleDelta(MapElement e, int length, boolean isLine) { int[] buffer = mIntBuffer; mReadBuffer.readSignedInt(buffer, length); - float[] outBuffer = mElem.ensurePointSize(mElem.pointPos + length, true); - int outPos = mElem.pointPos; + float[] outBuffer = e.ensurePointSize(e.pointPos + length, true); + int outPos = e.pointPos; int lat, lon; - // get the first way node latitude offset + /* get the first way node latitude offset */ int firstLat = lat = mTileLatitude + buffer[0]; - // get the first way node longitude offset + /* get the first way node longitude offset */ int firstLon = lon = mTileLongitude + buffer[1]; outBuffer[outPos++] = lon; @@ -619,45 +606,43 @@ public class MapDatabase implements ITileDataSource { lon += deltaLon; if (pos == length - 2) { - boolean line = (lon != firstLon && lat != firstLat); - // this also removes closed ways that are not polygon, - // but how do we know? + boolean line = isLine || (lon != firstLon && lat != firstLat); + /* this also removes closed ways that are not polygon, + * but how do we know? */ if (line) { outBuffer[outPos++] = lon; outBuffer[outPos++] = lat; cnt += 2; } - if (mElem.type == GeometryType.NONE) { - mElem.type = line ? GeometryType.LINE : GeometryType.POLY; + if (e.type == GeometryType.NONE) { + e.type = line ? GeometryType.LINE : GeometryType.POLY; } - } else if (deltaLon > minLon || deltaLon < -minLon - || deltaLat > minLat || deltaLat < -minLat) { + } else if (deltaLon > minDeltaLon || deltaLon < -minDeltaLon + || deltaLat > minDeltaLat || deltaLat < -minDeltaLat) { outBuffer[outPos++] = lon; outBuffer[outPos++] = lat; cnt += 2; } } - //mReductionCnt += length - cnt; - mElem.pointPos = outPos; - + e.pointPos = outPos; return cnt; } - private int decodeWayNodesSingleDelta(int length) { + private int decodeWayNodesSingleDelta(MapElement e, int length, boolean isLine) { int[] buffer = mIntBuffer; mReadBuffer.readSignedInt(buffer, length); - float[] outBuffer = mElem.ensurePointSize(mElem.pointPos + length, true); - int outPos = mElem.pointPos; + float[] outBuffer = e.ensurePointSize(e.pointPos + length, true); + int outPos = e.pointPos; int lat, lon; - // get the first way node latitude single-delta offset + /* get the first way node latitude single-delta offset */ int firstLat = lat = mTileLatitude + buffer[0]; - // get the first way node longitude single-delta offset + /* get the first way node longitude single-delta offset */ int firstLon = lon = mTileLongitude + buffer[1]; outBuffer[outPos++] = lon; @@ -672,7 +657,7 @@ public class MapDatabase implements ITileDataSource { lon += deltaLon; if (pos == length - 2) { - boolean line = (lon != firstLon && lat != firstLat); + boolean line = isLine || (lon != firstLon && lat != firstLat); if (line) { outBuffer[outPos++] = lon; @@ -680,19 +665,18 @@ public class MapDatabase implements ITileDataSource { cnt += 2; } - if (mElem.type == GeometryType.NONE) - mElem.type = line ? GeometryType.LINE : GeometryType.POLY; + if (e.type == GeometryType.NONE) + e.type = line ? GeometryType.LINE : GeometryType.POLY; - } else if (deltaLon > minLon || deltaLon < -minLon - || deltaLat > minLat || deltaLat < -minLat) { + } else if (deltaLon > minDeltaLon || deltaLon < -minDeltaLon + || deltaLat > minDeltaLat || deltaLat < -minDeltaLat) { outBuffer[outPos++] = lon; outBuffer[outPos++] = lat; cnt += 2; } } - //mReductionCnt += length - cnt; - mElem.pointPos = outPos; + e.pointPos = outPos; return cnt; } @@ -715,6 +699,8 @@ public class MapDatabase implements ITileDataSource { ITileDataSink mapDataSink, int numberOfWays) { Tag[] wayTags = mTileSource.fileInfo.wayTags; + MapElement e = mElem; + int numTags = 0; int wayDataBlocks; @@ -754,6 +740,7 @@ public class MapDatabase implements ITileDataSource { if (mCurrentRow < numRows) ymax = (int) (mCurrentRow * h + h); //log.debug(xmin + " " + ymin + " " + xmax + " " + ymax); + mTileClipper.setRect(xmin, ymin, xmax, ymax); } else { mTileClipper.setRect(-2, -2, Tile.SIZE + 2, Tile.SIZE + 2); @@ -786,7 +773,7 @@ public class MapDatabase implements ITileDataSource { byte numberOfTags = (byte) (mReadBuffer.readByte() & WAY_NUMBER_OF_TAGS_BITMASK); - if (!mReadBuffer.readTags(mElem.tags, wayTags, numberOfTags)) + if (!mReadBuffer.readTags(e.tags, wayTags, numberOfTags)) return false; numTags = numberOfTags; @@ -804,30 +791,30 @@ public class MapDatabase implements ITileDataSource { return false; } - // ignore the way tile bitmask (2 bytes) + /* ignore the way tile bitmask (2 bytes) */ mReadBuffer.skipBytes(2); } - // get the special byte which encodes multiple flags + /* get the special byte which encodes multiple flags */ byte specialByte = mReadBuffer.readByte(); - // bit 1-4 represent the layer + /* bit 1-4 represent the layer */ byte layer = (byte) ((specialByte & WAY_LAYER_BITMASK) >>> WAY_LAYER_SHIFT); - // bit 5-8 represent the number of tag IDs + /* bit 5-8 represent the number of tag IDs */ byte numberOfTags = (byte) (specialByte & WAY_NUMBER_OF_TAGS_BITMASK); if (numberOfTags != 0) { - if (!mReadBuffer.readTags(mElem.tags, wayTags, numberOfTags)) + if (!mReadBuffer.readTags(e.tags, wayTags, numberOfTags)) return false; numTags = numberOfTags; } - // get the feature bitmask (1 byte) + /* get the feature bitmask (1 byte) */ 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; @@ -835,36 +822,36 @@ public class MapDatabase implements ITileDataSource { boolean hasHouseNr = (featureByte & WAY_FEATURE_HOUSE_NUMBER) != 0; boolean hasRef = (featureByte & WAY_FEATURE_REF) != 0; - mElem.tags.numTags = numTags; + e.tags.numTags = numTags; if (mTileSource.experimental) { if (hasName) { int textPos = mReadBuffer.readUnsignedInt(); String str = mReadBuffer.readUTF8EncodedStringAt(stringOffset + textPos); - mElem.tags.add(new Tag(Tag.KEY_NAME, str, false)); + e.tags.add(new Tag(Tag.KEY_NAME, str, false)); } if (hasHouseNr) { int textPos = mReadBuffer.readUnsignedInt(); String str = mReadBuffer.readUTF8EncodedStringAt(stringOffset + textPos); - mElem.tags.add(new Tag(Tag.KEY_HOUSE_NUMBER, str, false)); + e.tags.add(new Tag(Tag.KEY_HOUSE_NUMBER, str, false)); } if (hasRef) { int textPos = mReadBuffer.readUnsignedInt(); String str = mReadBuffer.readUTF8EncodedStringAt(stringOffset + textPos); - mElem.tags.add(new Tag(Tag.KEY_REF, str, false)); + e.tags.add(new Tag(Tag.KEY_REF, str, false)); } } else { if (hasName) { String str = mReadBuffer.readUTF8EncodedString(); - mElem.tags.add(new Tag(Tag.KEY_NAME, str, false)); + e.tags.add(new Tag(Tag.KEY_NAME, str, false)); } if (hasHouseNr) { String str = mReadBuffer.readUTF8EncodedString(); - mElem.tags.add(new Tag(Tag.KEY_HOUSE_NUMBER, str, false)); + e.tags.add(new Tag(Tag.KEY_HOUSE_NUMBER, str, false)); } if (hasRef) { String str = mReadBuffer.readUTF8EncodedString(); - mElem.tags.add(new Tag(Tag.KEY_REF, str, false)); + e.tags.add(new Tag(Tag.KEY_REF, str, false)); } } if ((featureByte & WAY_FEATURE_LABEL_POSITION) != 0) @@ -883,42 +870,49 @@ public class MapDatabase implements ITileDataSource { wayDataBlocks = 1; } - for (int wayDataBlock = 0; wayDataBlock < wayDataBlocks; wayDataBlock++) { - mElem.clear(); + boolean linearFeature = e.tags.containsKey("highway") || + e.tags.containsKey("boundary") || + e.tags.containsKey("railway"); + if (linearFeature) { + Tag areaTag = e.tags.get("area"); + if (areaTag != null && areaTag.value == Tag.VALUE_YES) + linearFeature = false; + } - if (!processWayDataBlock(featureWayDoubleDeltaEncoding)) + for (int wayDataBlock = 0; wayDataBlock < wayDataBlocks; wayDataBlock++) { + e.clear(); + + if (!processWayDataBlock(e, featureWayDoubleDeltaEncoding, linearFeature)) return false; - if (mElem.isPoly() && mElem.index[0] < 6) { - //mSkipPoly++; + /* drop invalid outer ring */ + if (e.isPoly() && e.index[0] < 6) { continue; } - projectToTile(mElem); + mTileProjection.project(e); - if (mElem.isPoly()) { - if (!mTileClipper.clip(mElem)) { + if (e.isPoly()) { + if (!mTileClipper.clip(e)) { continue; } } - mElem.setLayer(layer); - mapDataSink.process(mElem); + e.setLayer(layer); + mapDataSink.process(e); } } return true; } - private final TileClipper mTileClipper = new TileClipper(-2, -2, Tile.SIZE + 2, Tile.SIZE + 2); - private float[] readOptionalLabelPosition() { float[] labelPosition = new float[2]; - // get the label position latitude offset (VBE-S) + /* get the label position latitude offset (VBE-S) */ labelPosition[1] = mTileLatitude + mReadBuffer.readSignedInt(); - // get the label position longitude offset (VBE-S) + /* get the label position longitude offset (VBE-S) */ labelPosition[0] = mTileLongitude + mReadBuffer.readSignedInt(); return labelPosition; @@ -960,65 +954,92 @@ public class MapDatabase implements ITileDataSource { return zoomTable; } - private static final double PI180 = (Math.PI / 180) / 1000000.0; - private static final double PIx4 = Math.PI * 4; + static class TileProjection { + private static final double COORD_SCALE = 1000000.0; - private boolean projectToTile(GeometryBuffer geom) { + long dx, dy; + double divx, divy; - float[] coords = geom.points; - int[] indices = geom.index; + void setTile(Tile tile) { + /* tile position in pixels at tile zoom */ + long x = tile.tileX * Tile.SIZE; + long y = tile.tileY * Tile.SIZE + Tile.SIZE; - long x = mTile.tileX * Tile.SIZE; - long y = mTile.tileY * Tile.SIZE + Tile.SIZE; - long z = Tile.SIZE << mTile.zoomLevel; + /* size of the map in pixel at tile zoom */ + long mapExtents = Tile.SIZE << tile.zoomLevel; - double divx, divy = 0; - long dx = (x - (z >> 1)); - long dy = (y - (z >> 1)); + /* offset relative to lat/lon == 0 */ + dx = (x - (mapExtents >> 1)); + dy = (y - (mapExtents >> 1)); - divx = 180000000.0 / (z >> 1); - divy = z / PIx4; + /* scales longitude(1e6) to map-pixel */ + divx = (180.0 * COORD_SCALE) / (mapExtents >> 1); - for (int pos = 0, outPos = 0, i = 0, m = indices.length; i < m; i++) { - int len = indices[i]; - if (len == 0) - continue; - if (len < 0) - break; - - int cnt = 0; - float lat, lon, prevLon = 0, prevLat = 0; - int first = outPos; - - for (int end = pos + len; pos < end; pos += 2) { - - lon = (float) ((coords[pos]) / divx - dx); - double sinLat = Math.sin(coords[pos + 1] * PI180); - lat = (float) (Tile.SIZE - (Math.log((1.0 + sinLat) / (1.0 - sinLat)) * divy + dy)); - - if (cnt != 0) { - // drop small distance intermediate nodes - if (lat == prevLat && lon == prevLon) { - //log.debug("drop zero delta "); - continue; - } - } - coords[outPos++] = prevLon = lon; - coords[outPos++] = prevLat = lat; - - cnt += 2; - } - - if (coords[first] == prevLon && coords[first + 1] == prevLat) { - //log.debug("drop closed"); - indices[i] = (short) (cnt - 2); - outPos -= 2; - } - else - indices[i] = (short) cnt; + /* scale latidute to map-pixel */ + divy = (mapExtents >> 1) / (Math.PI * 2.0); } - return true; - } + public void projectPoint(int lat, int lon, MapElement out) { + out.clear(); + out.startPoints(); + out.addPoint(projectLon(lon), projectLat(lat)); + } + public float projectLat(double lat) { + double s = Math.sin(lat * ((Math.PI / 180) / COORD_SCALE)); + double r = Math.log((1.0 + s) / (1.0 - s)); + + return Tile.SIZE - (float) (r * divy + dy); + } + + public float projectLon(double lon) { + return (float) (lon / divx - dx); + } + + void project(MapElement e) { + + float[] coords = e.points; + int[] indices = e.index; + + int inPos = 0; + int outPos = 0; + + for (int idx = 0, m = indices.length; idx < m; idx++) { + int len = indices[idx]; + if (len == 0) + continue; + if (len < 0) + break; + + float lat, lon, pLon = 0, pLat = 0; + int cnt = 0, first = outPos; + + for (int end = inPos + len; inPos < end; inPos += 2) { + lon = projectLon(coords[inPos]); + lat = projectLat(coords[inPos + 1]); + + if (cnt != 0) { + /* drop small distance intermediate nodes */ + if (lat == pLat && lon == pLon) { + //log.debug("drop zero delta "); + continue; + } + } + coords[outPos++] = pLon = lon; + coords[outPos++] = pLat = lat; + cnt += 2; + } + + if (coords[first] == pLon && coords[first + 1] == pLat) { + /* remove identical start/end point */ + log.debug("would drop closing point {}", e); + //indices[idx] = (short) (cnt - 2); + //outPos -= 2; + } + //else { + indices[idx] = (short) cnt; + //} + } + } + } }