diff --git a/docs/Changelog.md b/docs/Changelog.md index f00f0e24..063535ce 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -2,6 +2,7 @@ ## New since 0.14.0 +- Mapsforge: map stream support [#784](https://github.com/mapsforge/vtm/pull/784) - Render theme from Android content providers [#783](https://github.com/mapsforge/vtm/pull/783) - Many other minor improvements and bug fixes - [Solved issues](https://github.com/mapsforge/vtm/issues?q=is%3Aclosed+milestone%3A0.15.0) diff --git a/vtm/src/org/oscim/tiling/source/mapfile/IndexCache.java b/vtm/src/org/oscim/tiling/source/mapfile/IndexCache.java index 306f19fe..d8fd4a2f 100644 --- a/vtm/src/org/oscim/tiling/source/mapfile/IndexCache.java +++ b/vtm/src/org/oscim/tiling/source/mapfile/IndexCache.java @@ -1,5 +1,6 @@ /* * Copyright 2010, 2011, 2012 mapsforge.org + * Copyright 2017-2020 devemux86 * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * @@ -20,7 +21,8 @@ import org.oscim.tiling.source.mapfile.header.SubFileParameter; import org.oscim.utils.LRUCache; import java.io.IOException; -import java.io.RandomAccessFile; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.Collections; import java.util.Map; import java.util.logging.Level; @@ -44,15 +46,15 @@ class IndexCache { * SubFileParameter.BYTES_PER_INDEX_ENTRY; private final Map map; - private final RandomAccessFile randomAccessFile; + private final FileChannel fileChannel; /** - * @param randomAccessFile the map file from which the index should be read and cached. - * @param capacity the maximum number of entries in the cache. + * @param inputChannel the map file from which the index should be read and cached. + * @param capacity the maximum number of entries in the cache. * @throws IllegalArgumentException if the capacity is negative. */ - IndexCache(RandomAccessFile randomAccessFile, int capacity) { - this.randomAccessFile = randomAccessFile; + IndexCache(FileChannel inputChannel, int capacity) { + this.fileChannel = inputChannel; this.map = Collections.synchronizedMap(new LRUCache(capacity)); } @@ -97,11 +99,14 @@ class IndexCache { int remainingIndexSize = (int) (subFileParameter.indexEndAddress - indexBlockPosition); int indexBlockSize = Math.min(SIZE_OF_INDEX_BLOCK, remainingIndexSize); indexBlock = new byte[indexBlockSize]; + ByteBuffer indexBlockWrapper = ByteBuffer.wrap(indexBlock, 0, indexBlockSize); - this.randomAccessFile.seek(indexBlockPosition); - if (this.randomAccessFile.read(indexBlock, 0, indexBlockSize) != indexBlockSize) { - LOG.warning("reading the current index block has failed"); - return -1; + synchronized (this.fileChannel) { + this.fileChannel.position(indexBlockPosition); + if (this.fileChannel.read(indexBlockWrapper) != indexBlockSize) { + LOG.warning("reading the current index block has failed"); + return -1; + } } // put the index block in the map diff --git a/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java b/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java index d9bfa528..4385b9c0 100644 --- a/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java +++ b/vtm/src/org/oscim/tiling/source/mapfile/MapDatabase.java @@ -2,7 +2,7 @@ * Copyright 2010, 2011, 2012 mapsforge.org * Copyright 2013, 2014 Hannes Janetzek * Copyright 2014-2015 Ludwig M Brinckmann - * Copyright 2016-2019 devemux86 + * Copyright 2016-2020 devemux86 * Copyright 2016 Andrey Novikov * Copyright 2017-2018 Gustl22 * Copyright 2018 Bezzu @@ -38,8 +38,9 @@ import org.oscim.utils.geom.TileSeparator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.FileInputStream; import java.io.IOException; -import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -202,8 +203,7 @@ public class MapDatabase implements ITileDataSource { private long mFileSize; private boolean mDebugFile; - private RandomAccessFile mInputFile; - private ReadBuffer mReadBuffer; + private FileChannel mInputChannel; private String mSignatureBlock; private String mSignaturePoi; private String mSignatureWay; @@ -227,11 +227,15 @@ public class MapDatabase implements ITileDataSource { 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); - + // false positive: stream gets closed when the channel is closed + // see e.g. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4796385 + if (tileSource.mapFileInputStream != null) + mInputChannel = tileSource.mapFileInputStream.getChannel(); + else { + FileInputStream fis = new FileInputStream(tileSource.mapFile); + mInputChannel = fis.getChannel(); + } + mFileSize = mInputChannel.size(); } catch (IOException e) { log.error(e.getMessage()); /* make sure that the file is closed */ @@ -314,12 +318,10 @@ public class MapDatabase implements ITileDataSource { @Override public void dispose() { - mReadBuffer = null; - if (mInputFile != null) { - + if (mInputChannel != null) { try { - mInputFile.close(); - mInputFile = null; + mInputChannel.close(); + mInputChannel = null; } catch (IOException e) { log.error(e.getMessage()); } @@ -351,13 +353,13 @@ public class MapDatabase implements ITileDataSource { private void processBlock(QueryParameters queryParameters, SubFileParameter subFileParameter, ITileDataSink mapDataSink, BoundingBox boundingBox, Selector selector, - MapReadResult mapReadResult) { + MapReadResult mapReadResult, ReadBuffer readBuffer) { - if (!processBlockSignature()) { + if (!processBlockSignature(readBuffer)) { return; } - int[][] zoomTable = readZoomTable(subFileParameter); + int[][] zoomTable = readZoomTable(subFileParameter, readBuffer); if (zoomTable == null) { return; } @@ -366,7 +368,7 @@ public class MapDatabase implements ITileDataSource { int waysOnQueryZoomLevel = zoomTable[zoomTableRow][1]; /* get the relative offset to the first stored way in the block */ - int firstWayOffset = mReadBuffer.readUnsignedInt(); + int firstWayOffset = readBuffer.readUnsignedInt(); if (firstWayOffset < 0) { log.warn(INVALID_FIRST_WAY_OFFSET + firstWayOffset); if (mDebugFile) { @@ -376,8 +378,8 @@ public class MapDatabase implements ITileDataSource { } /* add the current buffer position to the relative first way offset */ - firstWayOffset += mReadBuffer.getBufferPosition(); - if (firstWayOffset > mReadBuffer.getBufferSize()) { + firstWayOffset += readBuffer.getBufferPosition(); + if (firstWayOffset > readBuffer.getBufferSize()) { log.warn(INVALID_FIRST_WAY_OFFSET + firstWayOffset); if (mDebugFile) { log.warn(DEBUG_SIGNATURE_BLOCK + mSignatureBlock); @@ -391,13 +393,13 @@ public class MapDatabase implements ITileDataSource { if (mapReadResult != null) pois = new ArrayList<>(); - if (!processPOIs(mapDataSink, poisOnQueryZoomLevel, boundingBox, filterRequired, pois)) { + if (!processPOIs(mapDataSink, poisOnQueryZoomLevel, boundingBox, filterRequired, pois, readBuffer)) { return; } /* finished reading POIs, check if the current buffer position is valid */ - if (mReadBuffer.getBufferPosition() > firstWayOffset) { - log.warn("invalid buffer position: " + mReadBuffer.getBufferPosition()); + if (readBuffer.getBufferPosition() > firstWayOffset) { + log.warn("invalid buffer position: " + readBuffer.getBufferPosition()); if (mDebugFile) { log.warn(DEBUG_SIGNATURE_BLOCK + mSignatureBlock); } @@ -405,13 +407,13 @@ public class MapDatabase implements ITileDataSource { } /* move the pointer to the first way */ - mReadBuffer.setBufferPosition(firstWayOffset); + readBuffer.setBufferPosition(firstWayOffset); List ways = null; if (mapReadResult != null && Selector.POIS != selector) ways = new ArrayList<>(); - if (!processWays(queryParameters, mapDataSink, waysOnQueryZoomLevel, boundingBox, filterRequired, selector, ways)) { + if (!processWays(queryParameters, mapDataSink, waysOnQueryZoomLevel, boundingBox, filterRequired, selector, ways, readBuffer)) { return; } @@ -576,10 +578,9 @@ public class MapDatabase implements ITileDataSource { } /* seek to the current block in the map file */ - mInputFile.seek(subFileParameter.startAddress + blockPointer); - /* read the current block into the buffer */ - if (!mReadBuffer.readFromFile(blockSize)) { + ReadBuffer readBuffer = new ReadBuffer(mInputChannel); + if (!readBuffer.readFromFile(subFileParameter.startAddress + blockPointer, blockSize)) { /* skip the current block */ log.warn("reading current block has failed: " + blockSize); return; @@ -596,7 +597,7 @@ public class MapDatabase implements ITileDataSource { mTileLatitude = (int) (tileLatitudeDeg * 1E6); mTileLongitude = (int) (tileLongitudeDeg * 1E6); - processBlock(queryParams, subFileParameter, mapDataSink, boundingBox, selector, mapReadResult); + processBlock(queryParams, subFileParameter, mapDataSink, boundingBox, selector, mapReadResult, readBuffer); } } } @@ -607,10 +608,10 @@ public class MapDatabase implements ITileDataSource { * @return true if the block signature could be processed successfully, * false otherwise. */ - private boolean processBlockSignature() { + private boolean processBlockSignature(ReadBuffer readBuffer) { if (mDebugFile) { /* get and check the block signature */ - mSignatureBlock = mReadBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_BLOCK); + mSignatureBlock = readBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_BLOCK); if (!mSignatureBlock.startsWith("###TileStart")) { log.warn("invalid block signature: " + mSignatureBlock); return false; @@ -628,7 +629,7 @@ public class MapDatabase implements ITileDataSource { * otherwise. */ private boolean processPOIs(ITileDataSink mapDataSink, int numberOfPois, BoundingBox boundingBox, - boolean filterRequired, List pois) { + boolean filterRequired, List pois, ReadBuffer readBuffer) { Tag[] poiTags = mTileSource.fileInfo.poiTags; MapElement e = mElem; @@ -638,7 +639,7 @@ public class MapDatabase implements ITileDataSource { if (mDebugFile) { /* get and check the POI signature */ - mSignaturePoi = mReadBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_POI); + mSignaturePoi = readBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_POI); if (!mSignaturePoi.startsWith("***POIStart")) { log.warn("invalid POI signature: " + mSignaturePoi); log.warn(DEBUG_SIGNATURE_BLOCK + mSignatureBlock); @@ -647,12 +648,12 @@ public class MapDatabase implements ITileDataSource { } /* get the POI latitude offset (VBE-S) */ - int latitude = mTileLatitude + mReadBuffer.readSignedInt(); + int latitude = mTileLatitude + readBuffer.readSignedInt(); /* get the POI longitude offset (VBE-S) */ - int longitude = mTileLongitude + mReadBuffer.readSignedInt(); + int longitude = mTileLongitude + readBuffer.readSignedInt(); /* get the special byte which encodes multiple flags */ - byte specialByte = mReadBuffer.readByte(); + byte specialByte = readBuffer.readByte(); /* bit 1-4 represent the layer */ byte layer = (byte) ((specialByte & POI_LAYER_BITMASK) >>> POI_LAYER_SHIFT); @@ -661,29 +662,29 @@ public class MapDatabase implements ITileDataSource { byte numberOfTags = (byte) (specialByte & POI_NUMBER_OF_TAGS_BITMASK); if (numberOfTags != 0) { - if (!mReadBuffer.readTags(e.tags, poiTags, numberOfTags)) + if (!readBuffer.readTags(e.tags, poiTags, numberOfTags)) return false; } /* get the feature bitmask (1 byte) */ - byte featureByte = mReadBuffer.readByte(); + byte featureByte = readBuffer.readByte(); /* bit 1-3 enable optional features * check if the POI has a name */ if ((featureByte & POI_FEATURE_NAME) != 0) { - String str = mTileSource.extractLocalized(mReadBuffer.readUTF8EncodedString()); + String str = mTileSource.extractLocalized(readBuffer.readUTF8EncodedString()); e.tags.add(new Tag(Tag.KEY_NAME, str, false)); } /* check if the POI has a house number */ if ((featureByte & POI_FEATURE_HOUSE_NUMBER) != 0) { - String str = mReadBuffer.readUTF8EncodedString(); + String str = readBuffer.readUTF8EncodedString(); e.tags.add(new Tag(Tag.KEY_HOUSE_NUMBER, str, false)); } /* check if the POI has an elevation */ if ((featureByte & POI_FEATURE_ELEVATION) != 0) { - String str = Integer.toString(mReadBuffer.readSignedInt()); + String str = Integer.toString(readBuffer.readSignedInt()); e.tags.add(new Tag(Tag.KEY_ELE, str, false)); } mTileProjection.projectPoint(latitude, longitude, e); @@ -712,9 +713,9 @@ public class MapDatabase implements ITileDataSource { return true; } - private boolean processWayDataBlock(MapElement e, boolean doubleDeltaEncoding, boolean isLine, List wayCoordinates, int[] labelPosition) { + private boolean processWayDataBlock(MapElement e, boolean doubleDeltaEncoding, boolean isLine, List wayCoordinates, int[] labelPosition, ReadBuffer readBuffer) { /* get and check the number of way coordinate blocks (VBE-U) */ - int numBlocks = mReadBuffer.readUnsignedInt(); + int numBlocks = readBuffer.readUnsignedInt(); if (numBlocks < 1 || numBlocks > Short.MAX_VALUE) { log.warn("invalid number of way coordinate blocks: " + numBlocks); return false; @@ -726,7 +727,7 @@ public class MapDatabase implements ITileDataSource { /* read the way coordinate blocks */ for (int coordinateBlock = 0; coordinateBlock < numBlocks; ++coordinateBlock) { - int numWayNodes = mReadBuffer.readUnsignedInt(); + int numWayNodes = readBuffer.readUnsignedInt(); if (numWayNodes < 2 || numWayNodes > Short.MAX_VALUE) { log.warn("invalid number of way nodes: " + numWayNodes); @@ -744,7 +745,7 @@ public class MapDatabase implements ITileDataSource { // label position must be set on first coordinate block wayLengths[coordinateBlock] = decodeWayNodes(doubleDeltaEncoding, e, len, isLine, - coordinateBlock == 0 ? labelPosition : null, waySegment); + coordinateBlock == 0 ? labelPosition : null, waySegment, readBuffer); if (wayCoordinates != null) wayCoordinates.add(waySegment); @@ -753,9 +754,9 @@ public class MapDatabase implements ITileDataSource { return true; } - private int decodeWayNodes(boolean doubleDelta, MapElement e, int length, boolean isLine, int[] labelPosition, GeoPoint[] waySegment) { + private int decodeWayNodes(boolean doubleDelta, MapElement e, int length, boolean isLine, int[] labelPosition, GeoPoint[] waySegment, ReadBuffer readBuffer) { int[] buffer = mIntBuffer; - mReadBuffer.readSignedInt(buffer, length); + readBuffer.readSignedInt(buffer, length); float[] outBuffer = e.ensurePointSize(e.pointNextPos + length, true); int outPos = e.pointNextPos; @@ -848,7 +849,7 @@ public class MapDatabase implements ITileDataSource { */ private boolean processWays(QueryParameters queryParameters, ITileDataSink mapDataSink, int numberOfWays, BoundingBox boundingBox, boolean filterRequired, - Selector selector, List ways) { + Selector selector, List ways, ReadBuffer readBuffer) { Tag[] wayTags = mTileSource.fileInfo.wayTags; MapElement e = mElem; @@ -860,9 +861,9 @@ public class MapDatabase implements ITileDataSource { stringOffset = 0; if (mTileSource.experimental) { - stringsSize = mReadBuffer.readUnsignedInt(); - stringOffset = mReadBuffer.getBufferPosition(); - mReadBuffer.skipBytes(stringsSize); + stringsSize = readBuffer.readUnsignedInt(); + stringOffset = readBuffer.getBufferPosition(); + readBuffer.skipBytes(stringsSize); } //setTileClipping(queryParameters); @@ -873,7 +874,7 @@ public class MapDatabase implements ITileDataSource { if (mDebugFile) { // get and check the way signature - mSignatureWay = mReadBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_WAY); + mSignatureWay = readBuffer.readUTF8EncodedString(SIGNATURE_LENGTH_WAY); if (!mSignatureWay.startsWith("---WayStart")) { log.warn("invalid way signature: " + mSignatureWay); log.warn(DEBUG_SIGNATURE_BLOCK + mSignatureBlock); @@ -882,7 +883,7 @@ public class MapDatabase implements ITileDataSource { } if (queryParameters.useTileBitmask) { - elementCounter = mReadBuffer.skipWays(queryParameters.queryTileBitmask, + elementCounter = readBuffer.skipWays(queryParameters.queryTileBitmask, elementCounter); if (elementCounter == 0) @@ -891,19 +892,19 @@ public class MapDatabase implements ITileDataSource { if (elementCounter < 0) return false; - if (mTileSource.experimental && mReadBuffer.lastTagPosition > 0) { - int pos = mReadBuffer.getBufferPosition(); - mReadBuffer.setBufferPosition(mReadBuffer.lastTagPosition); + if (mTileSource.experimental && readBuffer.lastTagPosition > 0) { + int pos = readBuffer.getBufferPosition(); + readBuffer.setBufferPosition(readBuffer.lastTagPosition); byte numberOfTags = - (byte) (mReadBuffer.readByte() & WAY_NUMBER_OF_TAGS_BITMASK); - if (!mReadBuffer.readTags(e.tags, wayTags, numberOfTags)) + (byte) (readBuffer.readByte() & WAY_NUMBER_OF_TAGS_BITMASK); + if (!readBuffer.readTags(e.tags, wayTags, numberOfTags)) return false; - mReadBuffer.setBufferPosition(pos); + readBuffer.setBufferPosition(pos); } } else { - int wayDataSize = mReadBuffer.readUnsignedInt(); + int wayDataSize = readBuffer.readUnsignedInt(); if (wayDataSize < 0) { log.warn("invalid way data size: " + wayDataSize); if (mDebugFile) { @@ -914,11 +915,11 @@ public class MapDatabase implements ITileDataSource { } /* ignore the way tile bitmask (2 bytes) */ - mReadBuffer.skipBytes(2); + readBuffer.skipBytes(2); } /* get the special byte which encodes multiple flags */ - byte specialByte = mReadBuffer.readByte(); + byte specialByte = readBuffer.readByte(); /* bit 1-4 represent the layer */ byte layer = (byte) ((specialByte & WAY_LAYER_BITMASK) >>> WAY_LAYER_SHIFT); @@ -926,12 +927,12 @@ public class MapDatabase implements ITileDataSource { byte numberOfTags = (byte) (specialByte & WAY_NUMBER_OF_TAGS_BITMASK); if (numberOfTags != 0) { - if (!mReadBuffer.readTags(e.tags, wayTags, numberOfTags)) + if (!readBuffer.readTags(e.tags, wayTags, numberOfTags)) return false; } /* get the feature bitmask (1 byte) */ - byte featureByte = mReadBuffer.readByte(); + byte featureByte = readBuffer.readByte(); /* bit 1-6 enable optional features */ boolean featureWayDoubleDeltaEncoding = @@ -943,42 +944,42 @@ public class MapDatabase implements ITileDataSource { if (mTileSource.experimental) { if (hasName) { - int textPos = mReadBuffer.readUnsignedInt(); - String str = mTileSource.extractLocalized(mReadBuffer.readUTF8EncodedStringAt(stringOffset + textPos)); + int textPos = readBuffer.readUnsignedInt(); + String str = mTileSource.extractLocalized(readBuffer.readUTF8EncodedStringAt(stringOffset + textPos)); e.tags.add(new Tag(Tag.KEY_NAME, str, false)); } if (hasHouseNr) { - int textPos = mReadBuffer.readUnsignedInt(); - String str = mReadBuffer.readUTF8EncodedStringAt(stringOffset + textPos); + int textPos = readBuffer.readUnsignedInt(); + String str = readBuffer.readUTF8EncodedStringAt(stringOffset + textPos); e.tags.add(new Tag(Tag.KEY_HOUSE_NUMBER, str, false)); } if (hasRef) { - int textPos = mReadBuffer.readUnsignedInt(); - String str = mReadBuffer.readUTF8EncodedStringAt(stringOffset + textPos); + int textPos = readBuffer.readUnsignedInt(); + String str = readBuffer.readUTF8EncodedStringAt(stringOffset + textPos); e.tags.add(new Tag(Tag.KEY_REF, str, false)); } } else { if (hasName) { - String str = mTileSource.extractLocalized(mReadBuffer.readUTF8EncodedString()); + String str = mTileSource.extractLocalized(readBuffer.readUTF8EncodedString()); e.tags.add(new Tag(Tag.KEY_NAME, str, false)); } if (hasHouseNr) { - String str = mReadBuffer.readUTF8EncodedString(); + String str = readBuffer.readUTF8EncodedString(); e.tags.add(new Tag(Tag.KEY_HOUSE_NUMBER, str, false)); } if (hasRef) { - String str = mReadBuffer.readUTF8EncodedString(); + String str = readBuffer.readUTF8EncodedString(); e.tags.add(new Tag(Tag.KEY_REF, str, false)); } } int[] labelPosition = null; if ((featureByte & WAY_FEATURE_LABEL_POSITION) != 0) { - labelPosition = readOptionalLabelPosition(); + labelPosition = readOptionalLabelPosition(readBuffer); } if ((featureByte & WAY_FEATURE_DATA_BLOCKS_BYTE) != 0) { - wayDataBlocks = mReadBuffer.readUnsignedInt(); + wayDataBlocks = readBuffer.readUnsignedInt(); if (wayDataBlocks < 1) { log.warn("invalid number of way data blocks: " + wayDataBlocks); @@ -999,7 +1000,7 @@ public class MapDatabase implements ITileDataSource { if (ways != null) wayNodes = new ArrayList<>(); - if (!processWayDataBlock(e, featureWayDoubleDeltaEncoding, linearFeature, wayNodes, labelPosition)) + if (!processWayDataBlock(e, featureWayDoubleDeltaEncoding, linearFeature, wayNodes, labelPosition, readBuffer)) return false; /* drop invalid outer ring */ @@ -1152,14 +1153,14 @@ public class MapDatabase implements ITileDataSource { return mapReadResult; } - private int[] readOptionalLabelPosition() { + private int[] readOptionalLabelPosition(ReadBuffer readBuffer) { int[] labelPosition = new int[2]; /* get the label position latitude offset (VBE-S) */ - labelPosition[1] = mReadBuffer.readSignedInt(); + labelPosition[1] = readBuffer.readSignedInt(); /* get the label position longitude offset (VBE-S) */ - labelPosition[0] = mReadBuffer.readSignedInt(); + labelPosition[0] = readBuffer.readSignedInt(); return labelPosition; } @@ -1187,7 +1188,7 @@ public class MapDatabase implements ITileDataSource { return readMapData(upperLeft, lowerRight, Selector.POIS); } - private int[][] readZoomTable(SubFileParameter subFileParameter) { + private int[][] readZoomTable(SubFileParameter subFileParameter, ReadBuffer readBuffer) { int rows = subFileParameter.zoomLevelMax - subFileParameter.zoomLevelMin + 1; int[][] zoomTable = new int[rows][2]; @@ -1195,8 +1196,8 @@ public class MapDatabase implements ITileDataSource { int cumulatedNumberOfWays = 0; for (int row = 0; row < rows; row++) { - cumulatedNumberOfPois += mReadBuffer.readUnsignedInt(); - cumulatedNumberOfWays += mReadBuffer.readUnsignedInt(); + cumulatedNumberOfPois += readBuffer.readUnsignedInt(); + cumulatedNumberOfWays += readBuffer.readUnsignedInt(); zoomTable[row][0] = cumulatedNumberOfPois; zoomTable[row][1] = cumulatedNumberOfWays; diff --git a/vtm/src/org/oscim/tiling/source/mapfile/MapFileTileSource.java b/vtm/src/org/oscim/tiling/source/mapfile/MapFileTileSource.java index 84aa4e2a..35ad5a15 100644 --- a/vtm/src/org/oscim/tiling/source/mapfile/MapFileTileSource.java +++ b/vtm/src/org/oscim/tiling/source/mapfile/MapFileTileSource.java @@ -1,7 +1,7 @@ /* * Copyright 2013 mapsforge.org * Copyright 2013 Hannes Janetzek - * Copyright 2016-2018 devemux86 + * Copyright 2016-2020 devemux86 * * This file is part of the OpenScienceMap project (http://www.opensciencemap.org). * @@ -25,13 +25,13 @@ import org.oscim.tiling.OverzoomTileDataSource; import org.oscim.tiling.TileSource; import org.oscim.tiling.source.mapfile.header.MapFileHeader; import org.oscim.tiling.source.mapfile.header.MapFileInfo; -import org.oscim.utils.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; -import java.io.RandomAccessFile; +import java.nio.channels.FileChannel; public class MapFileTileSource extends TileSource implements IMapFileTileSource { private static final Logger log = LoggerFactory.getLogger(MapFileTileSource.class); @@ -40,14 +40,14 @@ public class MapFileTileSource extends TileSource implements IMapFileTileSource * Amount of cache blocks that the index cache should store. */ private static final int INDEX_CACHE_SIZE = 64; - private static final String READ_ONLY_MODE = "r"; MapFileHeader fileHeader; MapFileInfo fileInfo; IndexCache databaseIndexCache; boolean experimental; File mapFile; - private RandomAccessFile mInputFile; + FileInputStream mapFileInputStream; + private FileChannel inputChannel; /** * The preferred language when extracting labels from this tile source. @@ -98,6 +98,10 @@ public class MapFileTileSource extends TileSource implements IMapFileTileSource return true; } + public void setMapFileInputStream(FileInputStream fileInputStream) { + this.mapFileInputStream = fileInputStream; + } + @Override public void setPreferredLanguage(String preferredLanguage) { this.preferredLanguage = preferredLanguage; @@ -105,31 +109,38 @@ public class MapFileTileSource extends TileSource implements IMapFileTileSource @Override public OpenResult open() { - if (!options.containsKey("file")) + if (mapFileInputStream == null && !options.containsKey("file")) return new OpenResult("no map file set"); try { - // make sure to close any previously opened file first - //close(); + // false positive: stream gets closed when the channel is closed + // see e.g. http://bugs.java.com/bugdatabase/view_bug.do?bug_id=4796385 + File file = null; + if (mapFileInputStream != null) + inputChannel = mapFileInputStream.getChannel(); + else { + // make sure to close any previously opened file first + //close(); - File file = new File(options.get("file")); + file = new File(options.get("file")); - // check if the file exists and is readable - if (!file.exists()) { - return new OpenResult("file does not exist: " + file); - } else if (!file.isFile()) { - return new OpenResult("not a file: " + file); - } else if (!file.canRead()) { - return new OpenResult("cannot read file: " + file); + // check if the file exists and is readable + if (!file.exists()) { + return new OpenResult("file does not exist: " + file); + } else if (!file.isFile()) { + return new OpenResult("not a file: " + file); + } else if (!file.canRead()) { + return new OpenResult("cannot read file: " + file); + } + + FileInputStream fis = new FileInputStream(file); + inputChannel = fis.getChannel(); } - - // open the file in read only mode - mInputFile = new RandomAccessFile(file, READ_ONLY_MODE); - long mFileSize = mInputFile.length(); - ReadBuffer mReadBuffer = new ReadBuffer(mInputFile); + long fileSize = inputChannel.size(); + ReadBuffer readBuffer = new ReadBuffer(inputChannel); fileHeader = new MapFileHeader(); - OpenResult openResult = fileHeader.readHeader(mReadBuffer, mFileSize); + OpenResult openResult = fileHeader.readHeader(readBuffer, fileSize); if (!openResult.isSuccess()) { close(); @@ -137,10 +148,7 @@ public class MapFileTileSource extends TileSource implements IMapFileTileSource } fileInfo = fileHeader.getMapFileInfo(); mapFile = file; - databaseIndexCache = new IndexCache(mInputFile, INDEX_CACHE_SIZE); - - // Experimental? - //experimental = fileInfo.fileVersion == 4; + databaseIndexCache = new IndexCache(inputChannel, INDEX_CACHE_SIZE); log.debug("File version: " + fileInfo.fileVersion); return OpenResult.SUCCESS; @@ -164,8 +172,14 @@ public class MapFileTileSource extends TileSource implements IMapFileTileSource @Override public void close() { - IOUtils.closeQuietly(mInputFile); - mInputFile = null; + if (inputChannel != null) { + try { + inputChannel.close(); + inputChannel = null; + } catch (IOException e) { + log.error(e.getMessage()); + } + } fileHeader = null; fileInfo = null; mapFile = null; diff --git a/vtm/src/org/oscim/tiling/source/mapfile/ReadBuffer.java b/vtm/src/org/oscim/tiling/source/mapfile/ReadBuffer.java index 0e565c5e..869d879b 100644 --- a/vtm/src/org/oscim/tiling/source/mapfile/ReadBuffer.java +++ b/vtm/src/org/oscim/tiling/source/mapfile/ReadBuffer.java @@ -25,6 +25,8 @@ import org.oscim.utils.Parameters; import java.io.IOException; import java.io.RandomAccessFile; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -39,12 +41,13 @@ public class ReadBuffer { private byte[] mBufferData; private int mBufferPosition; - private final RandomAccessFile mInputFile; + private ByteBuffer mBufferWrapper; + private final FileChannel mInputChannel; private final List mTagIds = new ArrayList<>(); - ReadBuffer(RandomAccessFile inputFile) { - mInputFile = inputFile; + ReadBuffer(FileChannel inputChannel) { + mInputChannel = inputChannel; } /** @@ -91,13 +94,54 @@ public class ReadBuffer { LOG.log(Level.SEVERE, t.getMessage(), t); return false; } + mBufferWrapper = ByteBuffer.wrap(mBufferData, 0, length); } mBufferPosition = 0; + mBufferWrapper.clear(); // reset the buffer position and read the data into the buffer // bufferPosition = 0; - return mInputFile.read(mBufferData, 0, length) == length; + return mInputChannel.read(mBufferWrapper) == length; + } + + /** + * Reads the given amount of bytes from the file into the read buffer and + * resets the internal buffer position. If + * the capacity of the read buffer is too small, a larger one is created + * automatically. + * + * @param offset the offset position, measured in bytes from the beginning of the file, at which to set the file pointer. + * @param length the amount of bytes to read from the file. + * @return true if the whole data was read successfully, false otherwise. + * @throws IOException if an error occurs while reading the file. + */ + public boolean readFromFile(long offset, int length) throws IOException { + // ensure that the read buffer is large enough + if (mBufferData == null || mBufferData.length < length) { + // ensure that the read buffer is not too large + if (length > Parameters.MAXIMUM_BUFFER_SIZE) { + LOG.warning("invalid read length: " + length); + return false; + } + try { + mBufferData = new byte[length]; + } catch (Throwable t) { + LOG.log(Level.SEVERE, t.getMessage(), t); + return false; + } + mBufferWrapper = ByteBuffer.wrap(mBufferData, 0, length); + } + + mBufferPosition = 0; + mBufferWrapper.clear(); + + // reset the buffer position and read the data into the buffer + // bufferPosition = 0; + synchronized (mInputChannel) { + mInputChannel.position(offset); + return mInputChannel.read(mBufferWrapper) == length; + } } /**