private void DownloadMap(MapType mapType, double longitude, double latitude, double zoom)
        {
            // Calculate map params
            m_MapType = mapType;
            m_Longitude = longitude;
            m_Latitude = latitude;
            m_Zoom = zoom;
            m_ptMapCenter = GoogleMapsNet.LatLongToPixel(latitude, longitude, zoom);
            m_rcViewport = new Rectangle(m_ptMapCenter.X - this.ClientRectangle.Width / 2, m_ptMapCenter.Y - this.ClientRectangle.Height / 2, this.ClientRectangle.Width, this.ClientRectangle.Height);
            Point pt1 = GoogleMapsNet.LatLongToPixel(latitude, longitude, zoom);
            Point pt2 = GoogleMapsNet.LatLongToPixel(latitude + 1, longitude + 1, zoom);
            if ((pt2.X - pt1.X) == 0)
            {
                m_LongitudeKoef = 0.0;
            }
            else
            {
                m_LongitudeKoef = Math.Abs(1.0 / (pt2.X - pt1.X));
            }
            if ((pt2.Y - pt1.Y) == 0)
            {
                m_LatitudeKoef = 0.0;
            }
            else
            {
                m_LatitudeKoef = Math.Abs(1.0 / (pt2.Y - pt1.Y));
            }
            int startx = m_rcViewport.Left / 256;
            int endx = m_rcViewport.Right / 256;
            int starty = m_rcViewport.Top / 256;
            int endy = m_rcViewport.Bottom / 256;
            m_rcTileBounds = new Rectangle(startx, starty, endx - startx, endy - starty);
            m_rcWorld = new Rectangle(m_rcTileBounds.Left * 256, m_rcTileBounds.Top * 256, (m_rcTileBounds.Right - m_rcTileBounds.Left + 1) * 256, (m_rcTileBounds.Bottom - m_rcTileBounds.Top + 1) * 256);

            // Download map tiles
            m_bDownload = true;
            int serverId = 0;
            for (int y = starty; y <= endy; y++)
            {
                for (int x = startx; x <= endx; x++)
                {
                    // Add new download info
                    DownloadInfo di = new DownloadInfo();
                    di.serverId = serverId;
                    di.url = BuildURL(mapType, serverId);
                    di.x = x;
                    di.y = y;
                    di.z = Convert.ToInt32(zoom);
                    di.bComplete = false;
                    di.time = DateTime.Now;
                    m_DownloadQue.Add(di);
                    serverId = (serverId + 1) % 4;
                }
            }
        }
        private void UpdateMap()
        {
            // Update viewport
            Size szOffset = new Size(m_ptEnd.X - m_ptStart.X, m_ptEnd.Y - m_ptStart.Y);
            m_rcViewport.Offset(-szOffset.Width, -szOffset.Height);

            // Update longitude and latitude
            m_Longitude += (-szOffset.Width * m_LongitudeKoef);
            m_Latitude += (szOffset.Height * m_LatitudeKoef);

            // Remove invisible tiles
            ArrayList tiles = new ArrayList();
            foreach (GoogleMapsTile tile in m_Tiles)
            {
                if (tile.IsVisible(m_rcViewport) == true)
                {
                    tiles.Add(tile);
                }
                else
                {
                    tile.Dispose();
                }
            }
            m_Tiles.Clear();
            m_Tiles = new ArrayList(tiles.ToArray());

            // Download new tiles
            int startx2 = m_rcViewport.Left / 256;
            int endx2 = m_rcViewport.Right / 256;
            int starty2 = m_rcViewport.Top / 256;
            int endy2 = m_rcViewport.Bottom / 256;
            m_rcTileBounds = new Rectangle(startx2, starty2, endx2 - startx2 + 1, endy2 - starty2 + 1);
            m_rcWorld = new Rectangle(m_rcTileBounds.Left * 256, m_rcTileBounds.Top * 256, (m_rcTileBounds.Right - m_rcTileBounds.Left + 1) * 256, (m_rcTileBounds.Bottom - m_rcTileBounds.Top + 1) * 256);
            int serverId = 0;
            for (int y = starty2; y <= endy2; y++)
            {
                for (int x = startx2; x <= endx2; x++)
                {
                    bool bExists = false;
                    foreach (GoogleMapsTile tile in m_Tiles)
                    {
                        if (tile.Match(x, y) == true)
                        {
                            bExists = true;
                            break;
                        }
                    }
                    foreach (DownloadInfo di in m_DownloadQue)
                    {
                        if ((di.x == x) && (di.y == y))
                        {
                            bExists = true;
                            break;
                        }
                    }
                    if (bExists == false)
                    {
                        // Add new download info
                        DownloadInfo di = new DownloadInfo();
                        di.serverId = serverId;
                        di.url = BuildURL(m_MapType, serverId);
                        di.x = x;
                        di.y = y;
                        di.z = Convert.ToInt32(m_Zoom);
                        di.bComplete = false;
                        di.time = DateTime.Now;
                        m_DownloadQue.Add(di);
                        serverId = (serverId + 1) % 4;
                    }
                }
            }

            // Update screen
            UpdateScreen();
        }