private IList <Way> ProcessWays(QueryParameters queryParameters, int numberOfWays, BoundingBox boundingBox, bool filterRequired, double tileLatitude, double tileLongitude, ReadBuffer readBuffer) { IList <Way> ways = new List <Way>(); Tag[] wayTags = this.mapFileHeader.MapFileInfo.WayTags; // Extend BoundingBox about wayFilterDistance meters, so that ways crossing the border added double verticalExpansion = PointUtils.LatitudeDistance(wayFilterDistance); double horizontalExpansion = PointUtils.LongitudeDistance(wayFilterDistance, Math.Max(Math.Abs(boundingBox.MinY), Math.Abs(boundingBox.MaxY))); double minLat = Math.Max(MercatorProjection.LATITUDE_MIN, boundingBox.MinY - verticalExpansion); double minLon = Math.Max(-180, boundingBox.MinX - horizontalExpansion); double maxLat = Math.Min(MercatorProjection.LATITUDE_MAX, boundingBox.MaxY + verticalExpansion); double maxLon = Math.Min(180, boundingBox.MaxX + horizontalExpansion); BoundingBox wayFilterBbox = new BoundingBox(minLon, minLat, maxLon, maxLat); for (int elementCounter = numberOfWays; elementCounter != 0; --elementCounter) { if (this.mapFileHeader.MapFileInfo.DebugFile) { // get and check the way signature string signatureWay = readBuffer.ReadUTF8EncodedString(SIGNATURE_LENGTH_WAY); if (!signatureWay.StartsWith("---WayStart", StringComparison.Ordinal)) { Logger.Log(LogLevel.Warning, "invalid way signature: " + signatureWay); return(null); } } // get the size of the way (VBE-U) int wayDataSize = readBuffer.ReadUnsignedInt(); if (wayDataSize < 0) { Logger.Log(LogLevel.Warning, "invalid way data size: " + wayDataSize); return(null); } if (queryParameters.useTileBitmask) { // get the way tile bitmask (2 bytes) int tileBitmask = readBuffer.ReadShort(); // check if the way is inside the requested tile if ((queryParameters.queryTileBitmask & tileBitmask) == 0) { // skip the rest of the way and continue with the next way readBuffer.SkipBytes(wayDataSize - 2); continue; } } else { // ignore the way tile bitmask (2 bytes) readBuffer.SkipBytes(2); } // get the special byte which encodes multiple flags sbyte specialByte = readBuffer.ReadByte(); // bit 1-4 represent the layer sbyte layer = (sbyte)((int)((uint)(specialByte & WAY_LAYER_BITMASK) >> WAY_LAYER_SHIFT)); // bit 5-8 represent the number of tag IDs sbyte numberOfTags = (sbyte)(specialByte & WAY_NUMBER_OF_TAGS_BITMASK); IList <Tag> tags = new List <Tag>(); for (sbyte tagIndex = numberOfTags; tagIndex != 0; --tagIndex) { int tagId = readBuffer.ReadUnsignedInt(); if (tagId < 0 || tagId >= wayTags.Length) { Logger.Log(LogLevel.Warning, "invalid way tag ID: " + tagId); return(null); } tags.Add(wayTags[tagId]); } // get the feature bitmask (1 byte) sbyte featureByte = readBuffer.ReadByte(); // bit 1-6 enable optional features bool featureName = (featureByte & WAY_FEATURE_NAME) != 0; bool featureHouseNumber = (featureByte & WAY_FEATURE_HOUSE_NUMBER) != 0; bool featureRef = (featureByte & WAY_FEATURE_REF) != 0; bool featureLabelPosition = (featureByte & WAY_FEATURE_LABEL_POSITION) != 0; bool featureWayDataBlocksByte = (featureByte & WAY_FEATURE_DATA_BLOCKS_BYTE) != 0; bool featureWayDoubleDeltaEncoding = (featureByte & WAY_FEATURE_DOUBLE_DELTA_ENCODING) != 0; // check if the way has a name if (featureName) { tags.Add(new Tag(TAG_KEY_NAME, ExtractLocalized(readBuffer.ReadUTF8EncodedString()))); } // check if the way has a house number if (featureHouseNumber) { tags.Add(new Tag(TAG_KEY_HOUSE_NUMBER, readBuffer.ReadUTF8EncodedString())); } // check if the way has a reference if (featureRef) { tags.Add(new Tag(TAG_KEY_REF, readBuffer.ReadUTF8EncodedString())); } Point labelPosition = ReadOptionalLabelPosition(tileLatitude, tileLongitude, featureLabelPosition, readBuffer); int wayDataBlocks = ReadOptionalWayDataBlocksByte(featureWayDataBlocksByte, readBuffer); if (wayDataBlocks < 1) { Logger.Log(LogLevel.Warning, "invalid number of way data blocks: " + wayDataBlocks); return(null); } for (int wayDataBlock = 0; wayDataBlock < wayDataBlocks; ++wayDataBlock) { List <List <Point> > wayNodes = ProcessWayDataBlock(tileLatitude, tileLongitude, featureWayDoubleDeltaEncoding, readBuffer); if (wayNodes != null) { if (filterRequired && wayFilterEnabled) { double minX = double.MaxValue; double minY = double.MaxValue; double maxX = 0; double maxY = 0; for (int i = 0; i < wayNodes.Count; i++) { for (int j = 0; j < wayNodes[i].Count; j++) { minX = Math.Min(minX, wayNodes[i][j].X); minY = Math.Min(minY, wayNodes[i][j].Y); maxX = Math.Max(maxX, wayNodes[i][j].X); maxY = Math.Max(maxY, wayNodes[i][j].Y); } } if (!wayFilterBbox.Intersects(new BoundingBox(minX, minY, maxX, maxY))) { continue; } } ways.Add(new Way(layer, tags, wayNodes, labelPosition)); } } } return(ways); }
private Point ReadOptionalLabelPosition(double tileLatitude, double tileLongitude, bool featureLabelPosition, ReadBuffer readBuffer) { if (featureLabelPosition) { // get the label position latitude offset (VBE-S) double latitude = tileLatitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); // get the label position longitude offset (VBE-S) double longitude = tileLongitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); return(new Point(longitude, latitude)); } return(null); }
private List <List <Point> > ProcessWayDataBlock(double tileLatitude, double tileLongitude, bool doubleDeltaEncoding, ReadBuffer readBuffer) { // get and check the number of way coordinate blocks (VBE-U) int numberOfWayCoordinateBlocks = readBuffer.ReadUnsignedInt(); if (numberOfWayCoordinateBlocks < 1 || numberOfWayCoordinateBlocks > short.MaxValue) { Logger.Log(LogLevel.Warning, "invalid number of way coordinate blocks: " + numberOfWayCoordinateBlocks); return(null); } // create the array which will store the different way coordinate blocks List <List <Point> > wayCoordinates = new List <List <Point> >(numberOfWayCoordinateBlocks); // read the way coordinate blocks for (int coordinateBlock = 0; coordinateBlock < numberOfWayCoordinateBlocks; ++coordinateBlock) { // get and check the number of way nodes (VBE-U) int numberOfWayNodes = readBuffer.ReadUnsignedInt(); if (numberOfWayNodes < 2 || numberOfWayNodes > short.MaxValue) { Logger.Log(LogLevel.Warning, "invalid number of way nodes: " + numberOfWayNodes); // returning null here will actually leave the tile blank as the // position on the ReadBuffer will not be advanced correctly. However, // it will not crash the app. return(null); } // create the array which will store the current way segment List <Point> waySegment = new List <Point>(numberOfWayNodes); if (doubleDeltaEncoding) { decodeWayNodesDoubleDelta(waySegment, tileLatitude, tileLongitude, readBuffer); } else { decodeWayNodesSingleDelta(waySegment, tileLatitude, tileLongitude, readBuffer); } wayCoordinates.Add(waySegment); } return(wayCoordinates); }
private IList <PointOfInterest> ProcessPOIs(double tileLatitude, double tileLongitude, int numberOfPois, BoundingBox boundingBox, bool filterRequired, ReadBuffer readBuffer) { IList <PointOfInterest> pois = new List <PointOfInterest>(); Tag[] poiTags = this.mapFileHeader.MapFileInfo.PoiTags; for (int elementCounter = numberOfPois; elementCounter != 0; --elementCounter) { if (this.mapFileHeader.MapFileInfo.DebugFile) { // get and check the POI signature string signaturePoi = readBuffer.ReadUTF8EncodedString(SIGNATURE_LENGTH_POI); if (!signaturePoi.StartsWith("***POIStart", StringComparison.Ordinal)) { Logger.Log(LogLevel.Warning, "invalid POI signature: " + signaturePoi); return(null); } } // get the POI latitude offset (VBE-S) double latitude = tileLatitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); // get the POI longitude offset (VBE-S) double longitude = tileLongitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); // get the special byte which encodes multiple flags sbyte specialByte = readBuffer.ReadByte(); // bit 1-4 represent the layer with sbyte layer = (sbyte)((int)((uint)(specialByte & POI_LAYER_BITMASK) >> POI_LAYER_SHIFT)); // bit 5-8 represent the number of tag IDs sbyte numberOfTags = (sbyte)(specialByte & POI_NUMBER_OF_TAGS_BITMASK); IList <Tag> tags = new List <Tag>(); // get the tag IDs (VBE-U) for (sbyte tagIndex = numberOfTags; tagIndex != 0; --tagIndex) { int tagId = readBuffer.ReadUnsignedInt(); if (tagId < 0 || tagId >= poiTags.Length) { Logger.Log(LogLevel.Warning, "invalid POI tag ID: " + tagId); return(null); } tags.Add(poiTags[tagId]); } // get the feature bitmask (1 byte) sbyte featureByte = readBuffer.ReadByte(); // bit 1-3 enable optional features bool featureName = (featureByte & POI_FEATURE_NAME) != 0; bool featureHouseNumber = (featureByte & POI_FEATURE_HOUSE_NUMBER) != 0; bool featureElevation = (featureByte & POI_FEATURE_ELEVATION) != 0; // check if the POI has a name if (featureName) { tags.Add(new Tag(TAG_KEY_NAME, ExtractLocalized(readBuffer.ReadUTF8EncodedString()))); } // check if the POI has a house number if (featureHouseNumber) { tags.Add(new Tag(TAG_KEY_HOUSE_NUMBER, readBuffer.ReadUTF8EncodedString())); } // check if the POI has an elevation if (featureElevation) { tags.Add(new Tag(TAG_KEY_ELE, Convert.ToString(readBuffer.ReadSignedInt()))); } Point position = new Point(longitude, latitude); // depending on the zoom level configuration the poi can lie outside // the tile requested, we filter them out here if (!filterRequired || boundingBox.Contains(position)) { pois.Add(new PointOfInterest(layer, tags, position)); } } return(pois); }
private MapReadResult ProcessBlocks(QueryParameters queryParameters, SubFileParameter subFileParameter, BoundingBox boundingBox) { bool queryIsWater = true; bool queryReadWaterInfo = false; MapReadResult mapFileReadResult = new MapReadResult(); // 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) { // calculate the actual block number of the needed block in the file long blockNumber = row * subFileParameter.BlocksWidth + column; // get the current index entry long currentBlockIndexEntry = this.databaseIndexCache.GetIndexEntry(subFileParameter, blockNumber); // 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 queryIsWater &= (currentBlockIndexEntry & BITMASK_INDEX_WATER) != 0; queryReadWaterInfo = true; } // get and check the current block pointer long currentBlockPointer = currentBlockIndexEntry & BITMASK_INDEX_OFFSET; if (currentBlockPointer < 1 || currentBlockPointer > subFileParameter.SubFileSize) { Logger.Log(LogLevel.Warning, "invalid current block pointer: " + currentBlockPointer); Logger.Log(LogLevel.Warning, "subFileSize: " + subFileParameter.SubFileSize); return(null); } long nextBlockPointer; // 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 nextBlockPointer = subFileParameter.SubFileSize; } else { // get and check the next block pointer nextBlockPointer = this.databaseIndexCache.GetIndexEntry(subFileParameter, blockNumber + 1) & BITMASK_INDEX_OFFSET; if (nextBlockPointer > subFileParameter.SubFileSize) { Logger.Log(LogLevel.Warning, "invalid next block pointer: " + nextBlockPointer); Logger.Log(LogLevel.Warning, "sub-file size: " + subFileParameter.SubFileSize); return(null); } } // calculate the size of the current block int currentBlockSize = (int)(nextBlockPointer - currentBlockPointer); if (currentBlockSize < 0) { Logger.Log(LogLevel.Warning, "current block size must not be negative: " + currentBlockSize); return(null); } else if (currentBlockSize == 0) { // the current block is empty, continue with the next block continue; } else if (currentBlockSize > ReadBuffer.MaximumBufferSize) { // the current block is too large, continue with the next block Logger.Log(LogLevel.Warning, "current block size too large: " + currentBlockSize); continue; } else if (currentBlockPointer + currentBlockSize > this.fileSize) { Logger.Log(LogLevel.Warning, "current block largher than file size: " + currentBlockSize); return(null); } // read the current block into the buffer ReadBuffer readBuffer = new ReadBuffer(inputStream); if (!readBuffer.ReadFromStream(subFileParameter.StartAddress + currentBlockPointer, currentBlockSize)) { // skip the current block Logger.Log(LogLevel.Warning, "reading current block has failed: " + currentBlockSize); return(null); } // calculate the top-left coordinates of the underlying tile double tileLatitude = MercatorProjection.TileYToLatitude(subFileParameter.BoundaryTileTop + row, subFileParameter.BaseZoomLevel); double tileLongitude = MercatorProjection.TileXToLongitude(subFileParameter.BoundaryTileLeft + column, subFileParameter.BaseZoomLevel); try { PoiWayBundle poiWayBundle = ProcessBlock(queryParameters, subFileParameter, boundingBox, tileLatitude, tileLongitude, readBuffer); if (poiWayBundle != null) { mapFileReadResult.Add(poiWayBundle); } } catch (System.IndexOutOfRangeException e) { Logger.Log(LogLevel.Error, e.Message, e); } } } // the query is finished, was the water flag set for all blocks? if (queryIsWater && queryReadWaterInfo) { // Deprecate water tiles rendering // mapFileReadResult.IsWater = true; } return(mapFileReadResult); }
private PoiWayBundle ProcessBlock(QueryParameters queryParameters, SubFileParameter subFileParameter, BoundingBox boundingBox, double tileLatitude, double tileLongitude, ReadBuffer readBuffer) { if (!ProcessBlockSignature(readBuffer)) { return(null); } int[][] zoomTable = ReadZoomTable(subFileParameter, readBuffer); int zoomTableRow = queryParameters.queryZoomLevel - subFileParameter.ZoomLevelMin; int poisOnQueryZoomLevel = zoomTable[zoomTableRow][0]; int waysOnQueryZoomLevel = zoomTable[zoomTableRow][1]; // get the relative offset to the first stored way in the block int firstWayOffset = readBuffer.ReadUnsignedInt(); if (firstWayOffset < 0) { Logger.Log(LogLevel.Warning, INVALID_FIRST_WAY_OFFSET + firstWayOffset); return(null); } // add the current buffer position to the relative first way offset firstWayOffset += readBuffer.BufferPosition; if (firstWayOffset > readBuffer.BufferSize) { Logger.Log(LogLevel.Warning, INVALID_FIRST_WAY_OFFSET + firstWayOffset); return(null); } bool filterRequired = queryParameters.queryZoomLevel > subFileParameter.BaseZoomLevel; IList <PointOfInterest> pois = ProcessPOIs(tileLatitude, tileLongitude, poisOnQueryZoomLevel, boundingBox, filterRequired, readBuffer); if (pois == null) { return(null); } // finished reading POIs, check if the current buffer position is valid if (readBuffer.BufferPosition > firstWayOffset) { Logger.Log(LogLevel.Warning, "invalid buffer position: " + readBuffer.BufferPosition); return(null); } // move the pointer to the first way readBuffer.BufferPosition = firstWayOffset; IList <Way> ways = ProcessWays(queryParameters, waysOnQueryZoomLevel, boundingBox, filterRequired, tileLatitude, tileLongitude, readBuffer); if (ways == null) { return(null); } return(new PoiWayBundle(pois, ways)); }
private void decodeWayNodesSingleDelta(List <Point> waySegment, double tileLatitude, double tileLongitude, ReadBuffer readBuffer) { // get the first way node latitude single-delta offset (VBE-S) double wayNodeLatitude = tileLatitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); // get the first way node longitude single-delta offset (VBE-S) double wayNodeLongitude = tileLongitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); // store the first way node waySegment.Add(new Point(wayNodeLongitude, wayNodeLatitude)); for (int wayNodesIndex = 1; wayNodesIndex < waySegment.Capacity; ++wayNodesIndex) { // get the way node latitude offset (VBE-S) wayNodeLatitude = wayNodeLatitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); // get the way node longitude offset (VBE-S) wayNodeLongitude = wayNodeLongitude + PointUtils.MicrodegreesToDegrees(readBuffer.ReadSignedInt()); // Decoding near international date line can return values slightly outside valid [-180°, 180°] due to calculation precision if (wayNodeLongitude < PointUtils.LONGITUDE_MIN && (PointUtils.LONGITUDE_MIN - wayNodeLongitude) < 0.001) { wayNodeLongitude = PointUtils.LONGITUDE_MIN; } else if (wayNodeLongitude > PointUtils.LONGITUDE_MAX && (wayNodeLongitude - PointUtils.LONGITUDE_MAX) < 0.001) { wayNodeLongitude = PointUtils.LONGITUDE_MAX; } waySegment.Add(new Point(wayNodeLongitude, wayNodeLatitude)); } }