Exemplo n.º 1
0
        private List <RenderInfo> getMatchedRenderInfo(RequestedTileInformation ti, POI poi)
        {
            List <RenderInfo> result = new List <RenderInfo>();

            //now the easy sloppy way
            for (int i = 0; i < poi.TagIDs.Count; i++)
            {
                getMatchedRenderInfo(_renderInfoList, result, "node", ti.mapFile.Header.POITags[poi.TagIDs[i]].Key, ti.mapFile.Header.POITags[poi.TagIDs[i]].Value, ti.Zoom);
            }
            return(result);
        }
Exemplo n.º 2
0
        public void Render(RequestedTileInformation ti, Graphics g, POI poi)
        {
            lock (this) //gdi objects are not threadsafe
            {
                try
                {
                    List <RenderInfo> ris = getMatchedRenderInfo(ti, poi);

                    bool cDone = false;
                    bool sDone = false;
                    foreach (RenderInfo ri in ris)
                    {
                        if (ri.tag == "circle")
                        {
                            break;
                        }
                    }
                    foreach (RenderInfo ri in ris)
                    {
                        if (ri.tag == "caption" && !cDone)
                        {
                            using (Font fnt = new Font(FontFamily.GenericSerif, string.IsNullOrEmpty(ri.font_size) ? (float)10.0 : (float)Utils.Conversion.StringToDouble(ri.font_size), ri.font_style == "bold" ? FontStyle.Bold : FontStyle.Regular))
                                using (StringFormat sf = new StringFormat())
                                {
                                    sf.Alignment     = StringAlignment.Center;
                                    sf.LineAlignment = StringAlignment.Center;
                                    float x = (float)toRelTileX(poi.Longitude, ti.X, ti.Zoom);
                                    float y = (float)toRelTileY(poi.Latitude, ti.Y, ti.Zoom);
                                    if (x > 0 && x < 256 && y > 0 && y < 256)
                                    {
                                        SizeF s  = g.MeasureString(poi.Name, fnt);
                                        float w2 = (s.Width / 2.0f);
                                        float h2 = (s.Height / 2.0f);
                                        if (x + w2 > 256)
                                        {
                                            x = 256 - w2;
                                        }
                                        if (y + h2 > 256)
                                        {
                                            y = 256 - h2;
                                        }
                                        if (x < w2)
                                        {
                                            x = w2;
                                        }
                                        if (y < h2)
                                        {
                                            y = h2;
                                        }
                                        g.DrawString(poi.Name, fnt, ri.Brush ?? _fixedTextBrush, x, y, sf);
                                        //g.DrawRectangle(new Pen(Color.Red, 1), x-w2, y-h2, s.Width, s.Height);
                                    }
                                }
                            cDone = true;
                        }
                        else if (ri.tag == "symbol" && !sDone && ri.Symbol != null)
                        {
                            g.DrawImageUnscaled(ri.Symbol, (int)toRelTileX(poi.Longitude, ti.X, ti.Zoom), (int)toRelTileY(poi.Latitude, ti.Y, ti.Zoom));
                            sDone = true;
                        }
                    }
                }
                catch
                {
                }
            }
        }
Exemplo n.º 3
0
        public RequestedTileInformation GetTileInformation(long x, long y, int zoom)
        {
            RequestedTileInformation ti = new RequestedTileInformation();
            try
            {
                ti.mapFile = _mapFile;
                ti.X = x;
                ti.Y = y;
                ti.Zoom = zoom;
                ti.ZoomIntervalConfig = this.ZoomIntervalConfig;

                if (zoom < ZoomIntervalConfig.BaseZoomLevel)
                {
                    // calculate the XY numbers of the upper left and lower right sub-tiles
                    int zoomLevelDifference = ZoomIntervalConfig.BaseZoomLevel - zoom;
                    ti.FromBaseTileX = x << zoomLevelDifference;
                    ti.FromBaseTileY = y << zoomLevelDifference;
                    ti.ToBaseTileX = ti.FromBaseTileX + (1 << zoomLevelDifference) - 1;
                    ti.ToBaseTileY = ti.FromBaseTileY + (1 << zoomLevelDifference) - 1;
                    ti.UseSubTileBitmapMask = false;
                }
                else if (zoom > ZoomIntervalConfig.BaseZoomLevel)
                {
                    // calculate the XY numbers of the parent base tile
                    int zoomLevelDifference = zoom - ZoomIntervalConfig.BaseZoomLevel;
                    ti.FromBaseTileX = x >> zoomLevelDifference;
                    ti.FromBaseTileY = y >> zoomLevelDifference;
                    ti.ToBaseTileX = ti.FromBaseTileX;
                    ti.ToBaseTileY = ti.FromBaseTileY;
                    ti.UseSubTileBitmapMask = true;

                    //calculate ti.SubTileBitmapMask
                    if (zoomLevelDifference == 1)
                    {
                        if (x % 2 == 0 && y % 2 == 0)
                        {
                            // upper left quadrant
                            ti.SubTileBitmapMask = 0xcc00;
                        }
                        else if (x % 2 == 1 && y % 2 == 0)
                        {
                            // upper right quadrant
                            ti.SubTileBitmapMask = 0x3300;
                        }
                        else if (x % 2 == 0 && y % 2 == 1)
                        {
                            // lower left quadrant
                            ti.SubTileBitmapMask = 0xcc;
                        }
                        else
                        {
                            // lower right quadrant
                            ti.SubTileBitmapMask = 0x33;
                        }
                    }
                    else
                    {
                        // calculate the XY numbers of the second level sub-tile
                        long subtileX = x >> (zoomLevelDifference - 2);
                        long subtileY = y >> (zoomLevelDifference - 2);

                        // calculate the XY numbers of the parent tile
                        long parentTileX = subtileX >> 1;
                        long parentTileY = subtileY >> 1;

                        // determine the correct bitmask for all 16 sub-tiles
                        if (parentTileX % 2 == 0 && parentTileY % 2 == 0)
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x8000;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x4000;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x800;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x400;
                            }
                        }
                        else if (parentTileX % 2 == 1 && parentTileY % 2 == 0)
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x2000;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x1000;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x200;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x100;
                            }
                        }
                        else if (parentTileX % 2 == 0 && parentTileY % 2 == 1)
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x80;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x40;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x8;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x4;
                            }
                        }
                        else
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x20;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x10;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x2;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x1;
                            }
                        }
                    }

                }
                else
                {
                    // use the tile XY numbers of the requested tile
                    ti.FromBaseTileX = x;
                    ti.FromBaseTileY = y;
                    ti.ToBaseTileX = ti.FromBaseTileX;
                    ti.ToBaseTileY = ti.FromBaseTileY;
                    ti.UseSubTileBitmapMask = false;
                }

                ti.FromBlockX = Math.Max(ti.FromBaseTileX - MinX, 0);
                ti.FromBlockY = Math.Max(ti.FromBaseTileY - MinY, 0);
                ti.ToBlockX = Math.Min(ti.ToBaseTileX - MinX, MaxX);
                ti.ToBlockY = Math.Min(ti.ToBaseTileY - MinY, MaxY);

                ti.POIs = new List<POI>();
                ti.Ways = new List<Way>();
                for (long row = ti.FromBlockY; row <= ti.ToBlockY; row++)
                {
                    for (long column = ti.FromBlockX; column <= ti.ToBlockX; column++)
                    {
                        // calculate the actual block number of the needed block in the file
                        long blockNumber = (row * (MaxX - MinX + 1)) + column;
                        if (blockNumber >= _tileAbsOffsets.Length || _tileAbsOffsets[blockNumber] == 0 || _tileAbsOffsets[blockNumber] >= ZoomIntervalConfig.SubFileSize)
                        {
                            continue;
                        }

                        long blockSize;
                        if (blockNumber < _tileAbsOffsets.Length - 1)
                        {
                            blockSize = _tileAbsOffsets[blockNumber + 1] - _tileAbsOffsets[blockNumber];
                        }
                        else
                        {
                            blockSize = ZoomIntervalConfig.SubFileSize - _tileAbsOffsets[blockNumber] + 1;
                        }
                        if (blockSize < 1)
                        {
                            continue;
                        }

                        ti.TileDataBuffer = new byte[blockSize];
                        _mapFile.ReadBuffer(ti.TileDataBuffer, _tileAbsOffsets[blockNumber] + ZoomIntervalConfig.AbsStartPositionSubFile, (int)blockSize);
                        using (ti.TileDataBufferStream = new System.IO.MemoryStream(ti.TileDataBuffer))
                        {
                            // calculate the top-left coordinates of the underlying tile
                            ti.TileLatitudeDeg = MapFile.GetLatitude(MinY + row, ZoomIntervalConfig.BaseZoomLevel);
                            ti.TileLongitudeDeg = MapFile.GetLongitude(MinX + column, ZoomIntervalConfig.BaseZoomLevel);
                            ti.TileLatitude = (int)(ti.TileLatitudeDeg * 1000000);
                            ti.TileLongitude = (int)(ti.TileLongitudeDeg * 1000000);

                            if (_mapFile.Header.DebugInformationPresent)
                            {
                                ti.TileDataBufferStream.Position = 32;
                            }

                            //get the zoom table
                            int rows = ZoomIntervalConfig.MaxZoomLevel - ZoomIntervalConfig.MinZoomLevel + 1;
                            ti.ZoomTable = new int[rows, 2];

                            int cumulatedNumberOfPois = 0;
                            int cumulatedNumberOfWays = 0;

                            for (int r = 0; r < rows; r++)
                            {
                                cumulatedNumberOfPois += (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                cumulatedNumberOfWays += (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);

                                ti.ZoomTable[r, 0] = cumulatedNumberOfPois;
                                ti.ZoomTable[r, 1] = cumulatedNumberOfWays;
                            }
                            ti.ZoomTableRow = zoom - ZoomIntervalConfig.MinZoomLevel;
                            ti.PoisOnQueryZoomLevel = ti.ZoomTable[ti.ZoomTableRow, 0];
                            ti.WaysOnQueryZoomLevel = ti.ZoomTable[ti.ZoomTableRow, 1];

                            ti.FirstWayOffset = MapFile.ReadVBEUint(ti.TileDataBufferStream);
                            ti.FirstWayOffset += ti.TileDataBufferStream.Position;

                            //get POIs
                            for (int p = 0; p < ti.PoisOnQueryZoomLevel; p++)
                            {
                                POI poi = new POI();

                                if (_mapFile.Header.DebugInformationPresent)
                                {
                                    ti.TileDataBufferStream.Position += 32;
                                }

                                //lat/lon
                                poi.Latitude = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                poi.Longitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                //layer and tag ids
                                poi.TagIDs = new List<int>();
                                byte b = (byte)ti.TileDataBufferStream.ReadByte();
                                poi.Layer = b >> 4;
                                int tagCount = (b & 0x0F);
                                for (int t = 0; t < tagCount; t++)
                                {
                                    poi.TagIDs.Add((int)MapFile.ReadVBEUint(ti.TileDataBufferStream));
                                }

                                //flags
                                b = (byte)ti.TileDataBufferStream.ReadByte();
                                if ((b & 0x80) == 0x80)
                                {
                                    poi.Name = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    poi.Name = null;
                                }
                                if ((b & 0x40) == 0x40)
                                {
                                    poi.HouseNumber = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    poi.HouseNumber = null;
                                }
                                if ((b & 0x20) == 0x20)
                                {
                                    poi.Elevation = MapFile.ReadVBESint(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    poi.Elevation = null;
                                }

                                ti.POIs.Add(poi);
                            }

                            ti.TileDataBufferStream.Position = ti.FirstWayOffset;

                            //get ways
                            for (int w = 0; w < ti.WaysOnQueryZoomLevel; w++)
                            {
                                Way way = new Way();

                                if (_mapFile.Header.DebugInformationPresent)
                                {
                                    ti.TileDataBufferStream.Position += 32;
                                }

                                int wayDataSize = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                int subTileBitmap = MapFile.ReadUInt16(ti.TileDataBufferStream);

                                if (ti.UseSubTileBitmapMask)
                                {
                                    // check if the way is inside the requested tile
                                    if ((ti.SubTileBitmapMask & subTileBitmap) == 0)
                                    {
                                        // skip the rest of the way and continue with the next way
                                        ti.TileDataBufferStream.Position += (wayDataSize - 2);
                                        continue;
                                    }
                                }

                                //layer and tags
                                way.TagIDs = new List<int>();
                                byte b = (byte)ti.TileDataBufferStream.ReadByte();
                                way.Layer = b >> 4;
                                int tagCount = (b & 0x0F);
                                for (int t = 0; t < tagCount; t++)
                                {
                                    way.TagIDs.Add((int)MapFile.ReadVBEUint(ti.TileDataBufferStream));
                                }

                                //feature bytes
                                b = (byte)ti.TileDataBufferStream.ReadByte();
                                if ((b & 0x80) == 0x80)
                                {
                                    way.Name = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.Name = null;
                                }
                                if ((b & 0x40) == 0x40)
                                {
                                    way.HouseNumber = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.HouseNumber = null;
                                }
                                if ((b & 0x20) == 0x20)
                                {
                                    way.Reference = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.Reference = null;
                                }
                                if ((b & 0x10) == 0x10)
                                {
                                    way.LabelLatitude = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                    way.LabelLongitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.LabelLatitude = null;
                                    way.LabelLongitude = null;
                                }

                                int numberOfBlocks;
                                if ((b & 0x08) == 0x08)
                                {
                                    numberOfBlocks = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    numberOfBlocks = 1;
                                }

                                bool doubleEncoding = ((b & 0x04) == 0x04);
                                way.WayDataBlocks = new List<Way.WayData>();
                                for (int wb = 0; wb < numberOfBlocks; wb++)
                                {
                                    //read: way data
                                    Way.WayData wd = new Way.WayData();

                                    int numberOfWayCoordBlocks = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                    wd.DataBlock = new List<Way.WayCoordinateBlock>();
                                    for (int wcd = 0; wcd < numberOfWayCoordBlocks; wcd++)
                                    {
                                        //read way coordinate block
                                        Way.WayCoordinateBlock coordBlock = new Way.WayCoordinateBlock();
                                        coordBlock.CoordBlock = new List<Way.WayCoordinate>();

                                        int nodesCount = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                        //Way.WayCoordinate diffwc = wc;
                                        if (doubleEncoding)
                                        {
                                            // get the first way node latitude single-delta offset (VBE-S)
                                            int wayNodeLatitude = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                            // get the first way node longitude single-delta offset (VBE-S)
                                            int wayNodeLongitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                            // store the first way node
                                            Way.WayCoordinate wc = new Way.WayCoordinate();
                                            wc.Latitude = wayNodeLatitude;
                                            wc.Longitude = wayNodeLongitude;
                                            coordBlock.CoordBlock.Add(wc);

                                            int previousSingleDeltaLatitude = 0;
                                            int previousSingleDeltaLongitude = 0;

                                            for (int n = 1; n < nodesCount; n++)
                                            {
                                                // get the way node latitude double-delta offset (VBE-S)
                                                int doubleDeltaLatitude = MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                // get the way node longitude double-delta offset (VBE-S)
                                                int doubleDeltaLongitude = MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                int singleDeltaLatitude = doubleDeltaLatitude + previousSingleDeltaLatitude;
                                                int singleDeltaLongitude = doubleDeltaLongitude + previousSingleDeltaLongitude;

                                                wayNodeLatitude = wayNodeLatitude + singleDeltaLatitude;
                                                wayNodeLongitude = wayNodeLongitude + singleDeltaLongitude;

                                                Way.WayCoordinate wcNext = new Way.WayCoordinate();
                                                wcNext.Latitude = wayNodeLatitude;
                                                wcNext.Longitude = wayNodeLongitude;
                                                coordBlock.CoordBlock.Add(wcNext);

                                                previousSingleDeltaLatitude = singleDeltaLatitude;
                                                previousSingleDeltaLongitude = singleDeltaLongitude;
                                            }
                                        }
                                        else
                                        {

                                            // get the first way node latitude single-delta offset (VBE-S)
                                            int wayNodeLatitude = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                            // get the first way node longitude single-delta offset (VBE-S)
                                            int wayNodeLongitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                            // store the first way node
                                            Way.WayCoordinate wc = new Way.WayCoordinate();
                                            wc.Latitude = wayNodeLatitude;
                                            wc.Longitude = wayNodeLongitude;
                                            coordBlock.CoordBlock.Add(wc);

                                            for (int n = 1; n < nodesCount; n++)
                                            {
                                                // get the way node latitude offset (VBE-S)
                                                wayNodeLatitude = wayNodeLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                // get the way node longitude offset (VBE-S)
                                                wayNodeLongitude = wayNodeLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                Way.WayCoordinate wcNext = new Way.WayCoordinate();
                                                wcNext.Latitude = wayNodeLatitude;
                                                wcNext.Longitude = wayNodeLongitude;
                                                coordBlock.CoordBlock.Add(wcNext);
                                            }
                                        }

                                        wd.DataBlock.Add(coordBlock);
                                    }

                                    way.WayDataBlocks.Add(wd);
                                }

                                ti.Ways.Add(way);
                            }
                        }
                    }
                }
                ti.TileDataBuffer = null; //memory can be released
            }
            catch
            {
                ti = null;
            }
            return ti;
        }
Exemplo n.º 4
0
        public void Render(RequestedTileInformation ti, Graphics g, POI poi)
        {
            lock (this) //gdi objects are not threadsafe
            {
                try
                {
                    List<RenderInfo> ris = getMatchedRenderInfo(ti, poi);

                    bool cDone = false;
                    bool sDone = false;
                    foreach (RenderInfo ri in ris)
                    {
                        if (ri.tag == "circle")
                        {
                            break;
                        }
                    }
                    foreach (RenderInfo ri in ris)
                    {
                        if (ri.tag == "caption" && !cDone)
                        {
                            using (Font fnt = new Font(FontFamily.GenericSerif, string.IsNullOrEmpty(ri.font_size) ? (float)10.0 : (float)Utils.Conversion.StringToDouble(ri.font_size), ri.font_style == "bold" ? FontStyle.Bold : FontStyle.Regular))
                            using (StringFormat sf = new StringFormat())
                            {
                                sf.Alignment = StringAlignment.Center;
                                sf.LineAlignment = StringAlignment.Center;
                                float x = (float)toRelTileX(poi.Longitude, ti.X, ti.Zoom);
                                float y = (float)toRelTileY(poi.Latitude, ti.Y, ti.Zoom);
                                if (x > 0 && x < 256 && y > 0 && y < 256)
                                {
                                    SizeF s = g.MeasureString(poi.Name, fnt);
                                    float w2 = (s.Width / 2.0f);
                                    float h2 = (s.Height / 2.0f);
                                    if (x + w2 > 256) x = 256 - w2;
                                    if (y + h2 > 256) y = 256 - h2;
                                    if (x < w2) x = w2;
                                    if (y < h2) y = h2;
                                    g.DrawString(poi.Name, fnt, ri.Brush ?? _fixedTextBrush, x, y, sf);
                                    //g.DrawRectangle(new Pen(Color.Red, 1), x-w2, y-h2, s.Width, s.Height);
                                }
                            }
                            cDone = true;
                        }
                        else if (ri.tag == "symbol" && !sDone && ri.Symbol != null)
                        {
                            g.DrawImageUnscaled(ri.Symbol, (int)toRelTileX(poi.Longitude, ti.X, ti.Zoom), (int)toRelTileY(poi.Latitude, ti.Y, ti.Zoom));
                            sDone = true;
                        }
                    }
                }
                catch
                {
                }
            }
        }
Exemplo n.º 5
0
 private List<RenderInfo> getMatchedRenderInfo(RequestedTileInformation ti, POI poi)
 {
     List<RenderInfo> result = new List<RenderInfo>();
     //now the easy sloppy way
     for (int i = 0; i < poi.TagIDs.Count; i++)
     {
         getMatchedRenderInfo(_renderInfoList, result, "node", ti.mapFile.Header.POITags[poi.TagIDs[i]].Key, ti.mapFile.Header.POITags[poi.TagIDs[i]].Value, ti.Zoom);
     }
     return result;
 }
Exemplo n.º 6
0
        public RequestedTileInformation GetTileInformation(long x, long y, int zoom)
        {
            RequestedTileInformation ti = new RequestedTileInformation();

            try
            {
                ti.mapFile            = _mapFile;
                ti.X                  = x;
                ti.Y                  = y;
                ti.Zoom               = zoom;
                ti.ZoomIntervalConfig = this.ZoomIntervalConfig;

                if (zoom < ZoomIntervalConfig.BaseZoomLevel)
                {
                    // calculate the XY numbers of the upper left and lower right sub-tiles
                    int zoomLevelDifference = ZoomIntervalConfig.BaseZoomLevel - zoom;
                    ti.FromBaseTileX        = x << zoomLevelDifference;
                    ti.FromBaseTileY        = y << zoomLevelDifference;
                    ti.ToBaseTileX          = ti.FromBaseTileX + (1 << zoomLevelDifference) - 1;
                    ti.ToBaseTileY          = ti.FromBaseTileY + (1 << zoomLevelDifference) - 1;
                    ti.UseSubTileBitmapMask = false;
                }
                else if (zoom > ZoomIntervalConfig.BaseZoomLevel)
                {
                    // calculate the XY numbers of the parent base tile
                    int zoomLevelDifference = zoom - ZoomIntervalConfig.BaseZoomLevel;
                    ti.FromBaseTileX        = x >> zoomLevelDifference;
                    ti.FromBaseTileY        = y >> zoomLevelDifference;
                    ti.ToBaseTileX          = ti.FromBaseTileX;
                    ti.ToBaseTileY          = ti.FromBaseTileY;
                    ti.UseSubTileBitmapMask = true;

                    //calculate ti.SubTileBitmapMask
                    if (zoomLevelDifference == 1)
                    {
                        if (x % 2 == 0 && y % 2 == 0)
                        {
                            // upper left quadrant
                            ti.SubTileBitmapMask = 0xcc00;
                        }
                        else if (x % 2 == 1 && y % 2 == 0)
                        {
                            // upper right quadrant
                            ti.SubTileBitmapMask = 0x3300;
                        }
                        else if (x % 2 == 0 && y % 2 == 1)
                        {
                            // lower left quadrant
                            ti.SubTileBitmapMask = 0xcc;
                        }
                        else
                        {
                            // lower right quadrant
                            ti.SubTileBitmapMask = 0x33;
                        }
                    }
                    else
                    {
                        // calculate the XY numbers of the second level sub-tile
                        long subtileX = x >> (zoomLevelDifference - 2);
                        long subtileY = y >> (zoomLevelDifference - 2);

                        // calculate the XY numbers of the parent tile
                        long parentTileX = subtileX >> 1;
                        long parentTileY = subtileY >> 1;

                        // determine the correct bitmask for all 16 sub-tiles
                        if (parentTileX % 2 == 0 && parentTileY % 2 == 0)
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x8000;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x4000;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x800;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x400;
                            }
                        }
                        else if (parentTileX % 2 == 1 && parentTileY % 2 == 0)
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x2000;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x1000;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x200;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x100;
                            }
                        }
                        else if (parentTileX % 2 == 0 && parentTileY % 2 == 1)
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x80;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x40;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x8;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x4;
                            }
                        }
                        else
                        {
                            if (subtileX % 2 == 0 && subtileY % 2 == 0)
                            {
                                // upper left sub-tile
                                ti.SubTileBitmapMask = 0x20;
                            }
                            else if (subtileX % 2 == 1 && subtileY % 2 == 0)
                            {
                                // upper right sub-tile
                                ti.SubTileBitmapMask = 0x10;
                            }
                            else if (subtileX % 2 == 0 && subtileY % 2 == 1)
                            {
                                // lower left sub-tile
                                ti.SubTileBitmapMask = 0x2;
                            }
                            else
                            {
                                // lower right sub-tile
                                ti.SubTileBitmapMask = 0x1;
                            }
                        }
                    }
                }
                else
                {
                    // use the tile XY numbers of the requested tile
                    ti.FromBaseTileX        = x;
                    ti.FromBaseTileY        = y;
                    ti.ToBaseTileX          = ti.FromBaseTileX;
                    ti.ToBaseTileY          = ti.FromBaseTileY;
                    ti.UseSubTileBitmapMask = false;
                }

                ti.FromBlockX = Math.Max(ti.FromBaseTileX - MinX, 0);
                ti.FromBlockY = Math.Max(ti.FromBaseTileY - MinY, 0);
                ti.ToBlockX   = Math.Min(ti.ToBaseTileX - MinX, MaxX);
                ti.ToBlockY   = Math.Min(ti.ToBaseTileY - MinY, MaxY);

                ti.POIs = new List <POI>();
                ti.Ways = new List <Way>();
                for (long row = ti.FromBlockY; row <= ti.ToBlockY; row++)
                {
                    for (long column = ti.FromBlockX; column <= ti.ToBlockX; column++)
                    {
                        // calculate the actual block number of the needed block in the file
                        long blockNumber = (row * (MaxX - MinX + 1)) + column;
                        if (blockNumber >= _tileAbsOffsets.Length || _tileAbsOffsets[blockNumber] == 0 || _tileAbsOffsets[blockNumber] >= ZoomIntervalConfig.SubFileSize)
                        {
                            continue;
                        }

                        long blockSize;
                        if (blockNumber < _tileAbsOffsets.Length - 1)
                        {
                            blockSize = _tileAbsOffsets[blockNumber + 1] - _tileAbsOffsets[blockNumber];
                        }
                        else
                        {
                            blockSize = ZoomIntervalConfig.SubFileSize - _tileAbsOffsets[blockNumber] + 1;
                        }
                        if (blockSize < 1)
                        {
                            continue;
                        }

                        ti.TileDataBuffer = new byte[blockSize];
                        _mapFile.ReadBuffer(ti.TileDataBuffer, _tileAbsOffsets[blockNumber] + ZoomIntervalConfig.AbsStartPositionSubFile, (int)blockSize);
                        using (ti.TileDataBufferStream = new System.IO.MemoryStream(ti.TileDataBuffer))
                        {
                            // calculate the top-left coordinates of the underlying tile
                            ti.TileLatitudeDeg  = MapFile.GetLatitude(MinY + row, ZoomIntervalConfig.BaseZoomLevel);
                            ti.TileLongitudeDeg = MapFile.GetLongitude(MinX + column, ZoomIntervalConfig.BaseZoomLevel);
                            ti.TileLatitude     = (int)(ti.TileLatitudeDeg * 1000000);
                            ti.TileLongitude    = (int)(ti.TileLongitudeDeg * 1000000);

                            if (_mapFile.Header.DebugInformationPresent)
                            {
                                ti.TileDataBufferStream.Position = 32;
                            }

                            //get the zoom table
                            int rows = ZoomIntervalConfig.MaxZoomLevel - ZoomIntervalConfig.MinZoomLevel + 1;
                            ti.ZoomTable = new int[rows, 2];

                            int cumulatedNumberOfPois = 0;
                            int cumulatedNumberOfWays = 0;

                            for (int r = 0; r < rows; r++)
                            {
                                cumulatedNumberOfPois += (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                cumulatedNumberOfWays += (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);

                                ti.ZoomTable[r, 0] = cumulatedNumberOfPois;
                                ti.ZoomTable[r, 1] = cumulatedNumberOfWays;
                            }
                            ti.ZoomTableRow         = zoom - ZoomIntervalConfig.MinZoomLevel;
                            ti.PoisOnQueryZoomLevel = ti.ZoomTable[ti.ZoomTableRow, 0];
                            ti.WaysOnQueryZoomLevel = ti.ZoomTable[ti.ZoomTableRow, 1];

                            ti.FirstWayOffset  = MapFile.ReadVBEUint(ti.TileDataBufferStream);
                            ti.FirstWayOffset += ti.TileDataBufferStream.Position;

                            //get POIs
                            for (int p = 0; p < ti.PoisOnQueryZoomLevel; p++)
                            {
                                POI poi = new POI();

                                if (_mapFile.Header.DebugInformationPresent)
                                {
                                    ti.TileDataBufferStream.Position += 32;
                                }

                                //lat/lon
                                poi.Latitude  = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                poi.Longitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                //layer and tag ids
                                poi.TagIDs = new List <int>();
                                byte b = (byte)ti.TileDataBufferStream.ReadByte();
                                poi.Layer = b >> 4;
                                int tagCount = (b & 0x0F);
                                for (int t = 0; t < tagCount; t++)
                                {
                                    poi.TagIDs.Add((int)MapFile.ReadVBEUint(ti.TileDataBufferStream));
                                }

                                //flags
                                b = (byte)ti.TileDataBufferStream.ReadByte();
                                if ((b & 0x80) == 0x80)
                                {
                                    poi.Name = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    poi.Name = null;
                                }
                                if ((b & 0x40) == 0x40)
                                {
                                    poi.HouseNumber = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    poi.HouseNumber = null;
                                }
                                if ((b & 0x20) == 0x20)
                                {
                                    poi.Elevation = MapFile.ReadVBESint(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    poi.Elevation = null;
                                }

                                ti.POIs.Add(poi);
                            }

                            ti.TileDataBufferStream.Position = ti.FirstWayOffset;

                            //get ways
                            for (int w = 0; w < ti.WaysOnQueryZoomLevel; w++)
                            {
                                Way way = new Way();

                                if (_mapFile.Header.DebugInformationPresent)
                                {
                                    ti.TileDataBufferStream.Position += 32;
                                }

                                int wayDataSize   = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                int subTileBitmap = MapFile.ReadUInt16(ti.TileDataBufferStream);

                                if (ti.UseSubTileBitmapMask)
                                {
                                    // check if the way is inside the requested tile
                                    if ((ti.SubTileBitmapMask & subTileBitmap) == 0)
                                    {
                                        // skip the rest of the way and continue with the next way
                                        ti.TileDataBufferStream.Position += (wayDataSize - 2);
                                        continue;
                                    }
                                }

                                //layer and tags
                                way.TagIDs = new List <int>();
                                byte b = (byte)ti.TileDataBufferStream.ReadByte();
                                way.Layer = b >> 4;
                                int tagCount = (b & 0x0F);
                                for (int t = 0; t < tagCount; t++)
                                {
                                    way.TagIDs.Add((int)MapFile.ReadVBEUint(ti.TileDataBufferStream));
                                }

                                //feature bytes
                                b = (byte)ti.TileDataBufferStream.ReadByte();
                                if ((b & 0x80) == 0x80)
                                {
                                    way.Name = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.Name = null;
                                }
                                if ((b & 0x40) == 0x40)
                                {
                                    way.HouseNumber = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.HouseNumber = null;
                                }
                                if ((b & 0x20) == 0x20)
                                {
                                    way.Reference = MapFile.ReadUTF8EncodedString(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.Reference = null;
                                }
                                if ((b & 0x10) == 0x10)
                                {
                                    way.LabelLatitude  = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                    way.LabelLongitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    way.LabelLatitude  = null;
                                    way.LabelLongitude = null;
                                }

                                int numberOfBlocks;
                                if ((b & 0x08) == 0x08)
                                {
                                    numberOfBlocks = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                }
                                else
                                {
                                    numberOfBlocks = 1;
                                }

                                bool doubleEncoding = ((b & 0x04) == 0x04);
                                way.WayDataBlocks = new List <Way.WayData>();
                                for (int wb = 0; wb < numberOfBlocks; wb++)
                                {
                                    //read: way data
                                    Way.WayData wd = new Way.WayData();

                                    int numberOfWayCoordBlocks = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                    wd.DataBlock = new List <Way.WayCoordinateBlock>();
                                    for (int wcd = 0; wcd < numberOfWayCoordBlocks; wcd++)
                                    {
                                        //read way coordinate block
                                        Way.WayCoordinateBlock coordBlock = new Way.WayCoordinateBlock();
                                        coordBlock.CoordBlock = new List <Way.WayCoordinate>();

                                        int nodesCount = (int)MapFile.ReadVBEUint(ti.TileDataBufferStream);
                                        //Way.WayCoordinate diffwc = wc;
                                        if (doubleEncoding)
                                        {
                                            // get the first way node latitude single-delta offset (VBE-S)
                                            int wayNodeLatitude = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                            // get the first way node longitude single-delta offset (VBE-S)
                                            int wayNodeLongitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                            // store the first way node
                                            Way.WayCoordinate wc = new Way.WayCoordinate();
                                            wc.Latitude  = wayNodeLatitude;
                                            wc.Longitude = wayNodeLongitude;
                                            coordBlock.CoordBlock.Add(wc);

                                            int previousSingleDeltaLatitude  = 0;
                                            int previousSingleDeltaLongitude = 0;

                                            for (int n = 1; n < nodesCount; n++)
                                            {
                                                // get the way node latitude double-delta offset (VBE-S)
                                                int doubleDeltaLatitude = MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                // get the way node longitude double-delta offset (VBE-S)
                                                int doubleDeltaLongitude = MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                int singleDeltaLatitude  = doubleDeltaLatitude + previousSingleDeltaLatitude;
                                                int singleDeltaLongitude = doubleDeltaLongitude + previousSingleDeltaLongitude;

                                                wayNodeLatitude  = wayNodeLatitude + singleDeltaLatitude;
                                                wayNodeLongitude = wayNodeLongitude + singleDeltaLongitude;

                                                Way.WayCoordinate wcNext = new Way.WayCoordinate();
                                                wcNext.Latitude  = wayNodeLatitude;
                                                wcNext.Longitude = wayNodeLongitude;
                                                coordBlock.CoordBlock.Add(wcNext);

                                                previousSingleDeltaLatitude  = singleDeltaLatitude;
                                                previousSingleDeltaLongitude = singleDeltaLongitude;
                                            }
                                        }
                                        else
                                        {
                                            // get the first way node latitude single-delta offset (VBE-S)
                                            int wayNodeLatitude = ti.TileLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);
                                            // get the first way node longitude single-delta offset (VBE-S)
                                            int wayNodeLongitude = ti.TileLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                            // store the first way node
                                            Way.WayCoordinate wc = new Way.WayCoordinate();
                                            wc.Latitude  = wayNodeLatitude;
                                            wc.Longitude = wayNodeLongitude;
                                            coordBlock.CoordBlock.Add(wc);

                                            for (int n = 1; n < nodesCount; n++)
                                            {
                                                // get the way node latitude offset (VBE-S)
                                                wayNodeLatitude = wayNodeLatitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                // get the way node longitude offset (VBE-S)
                                                wayNodeLongitude = wayNodeLongitude + MapFile.ReadVBESint(ti.TileDataBufferStream);

                                                Way.WayCoordinate wcNext = new Way.WayCoordinate();
                                                wcNext.Latitude  = wayNodeLatitude;
                                                wcNext.Longitude = wayNodeLongitude;
                                                coordBlock.CoordBlock.Add(wcNext);
                                            }
                                        }

                                        wd.DataBlock.Add(coordBlock);
                                    }

                                    way.WayDataBlocks.Add(wd);
                                }

                                ti.Ways.Add(way);
                            }
                        }
                    }
                }
                ti.TileDataBuffer = null; //memory can be released
            }
            catch
            {
                ti = null;
            }
            return(ti);
        }