/// <summary> /// Fetch the Image Tile from given Tile Server <see cref="TileServer" /> /// </summary> /// <param name = "SourceTile">Source Tile <see cref="Tile"/></param> /// <param name = "SourceTileServer">The source Tile Server where Tile Images would be grabbed from <see cref="TileServer"/></param> /// <param name = "Subdomain">Subdomain of Source Tile Server to get the image from <see cref="TileServer" /></param> /// <param name = "Zoom">Zoom Level <see href="http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames#Zoom_levels" /> </param> /// <param name = "XOffset">(Optional) Image Offest in the X-Axis (e.g. Use 1 if you want to retrieve the tile image above the Source Tile <see cref="Tile"/></param> /// <param name = "YOffset">(Optional) Image Offest in the Y-Axis (e.g. Use 1 if you want to retrieve the tile image to the right of the Source Tile <see cref="Tile"/> </param> /// <param name = "Timeout">(Optional) Optional timeout in milliseconds /> </param> /// <returns>Tile Image as a Byte Array</returns> public static byte[] FetchTile(Tile SourceTile, TileServer SourceTileServer, char Subdomain, int Zoom = 0, short XOffset = 0, short YOffset = 0, int Timeout = 10000) { var tileUri = GetTileUri(SourceTileServer, Subdomain, Zoom, (int)(SourceTile.X) + XOffset, (int)(SourceTile.Y) + YOffset); var tileRequest = (HttpWebRequest)WebRequest.Create(tileUri); tileRequest.UseDefaultCredentials = true; System.Diagnostics.Debug.WriteLine(tileRequest.RequestUri.ToString()); try { using (var serverResponse = GetSyncResponse(tileRequest, Timeout)) { if (serverResponse == null) { throw (new WebException("Error fetching tile from: " + tileUri.OriginalString, null)); } else { if (serverResponse.ContentType.StartsWith("image", StringComparison.OrdinalIgnoreCase)) { using (Stream responseStream = serverResponse.GetResponseStream()) { return(ReadFully(responseStream)); } } else { throw (new WebException("Error fetching tile from: " + tileUri.OriginalString + Environment.NewLine + "Instead of an image tile, the following content type " + Environment.NewLine + "was returned instead: " + serverResponse.ContentType + Environment.NewLine + (serverResponse.ContentType.StartsWith("text", StringComparison.OrdinalIgnoreCase) ? ReadAllText(serverResponse.GetResponseStream()) : "") , null)); } } } } catch (Exception ex) { throw (new WebException("Error fetching tile from: " + tileUri.OriginalString + Environment.NewLine + ex.Message, null)); } }
/// <summary> /// Creates a Tile Cache in MBTiles v1.1 Format <see href="https://github.com/mapbox/mbtiles-spec" /> /// from a given Bounding Box (SouthEast Lat/Lon, NorthWest Lat/Lon, Minimum Zoom and Maximum Zoom) /// </summary> /// <param name = "SourceTileServer">The source Tile Server where Tile Images would be grabbed from <see cref="TileServer"/></param> /// <param name = "SqlConnection">A valid SQLiteConnection based on the SQLite PCL-Net</param> /// <param name = "SELat">Latitude of SouthEast corner of map extent</param> /// <param name = "SELon">Longitude of SouthEast corner of map extent</param> /// <param name = "NWLat">Latitude of NorthWest corner of map extent</param> /// <param name = "NWLon">Longitude of NorthWest corner of map extent</param> /// <param name = "MinZoom">Minimum map Zoom Level</param> /// <param name = "MaxZoom">(Optional) Maximum map Zoom Level</param> /// <returns>Generates an MBTiles File in the specified SQLiteConnection</returns> public static void CreateTileCache(TileServer SourceTileServer, SQLiteConnection SqlConnection, float SELat, float SELon, float NWLat, float NWLon, ushort MinZoom, ushort MaxZoom = 100) { // Calculate Maximum Zoom Level if (MaxZoom > SourceTileServer.MaxZoom) { MaxZoom = SourceTileServer.MaxZoom; } // Check for valid Zoom Level if (MinZoom > MaxZoom) { throw new Exception("Minimum Zoom should be less than Maximum Zoom"); } // Get number of subdomains for given Tile Server int s = 0; // Define starting subdomain int sMax = SourceTileServer.Subdomains.Count() - 1; // Setup SQLite Connection SQLiteCommand sqlCommand; string sql; // Create Metadata Table sql = "CREATE TABLE metadata (name text, value text)"; sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); // Create Tiles Table sql = "CREATE TABLE tiles (zoom_level integer, tile_column integer, tile_row integer, tile_data blob)"; sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); // Insert Required Metadata sql = "INSERT INTO metadata VALUES ('name','Test')"; sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); sql = "INSERT INTO metadata VALUES ('type','baselayer')"; sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); sql = "INSERT INTO metadata VALUES ('version','1')"; sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); sql = "INSERT INTO metadata VALUES ('description','Test Tileset')"; sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); sql = string.Format("INSERT INTO metadata VALUES ('format','{0}')", SourceTileServer.imgFormat); sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); sql = string.Format("INSERT INTO metadata VALUES ('bounds','{0},{1},{2},{3}')", float.Parse(SELon.ToString(), CultureInfo.InvariantCulture), float.Parse(SELat.ToString(), CultureInfo.InvariantCulture), float.Parse(NWLon.ToString(), CultureInfo.InvariantCulture), float.Parse(NWLat.ToString(), CultureInfo.InvariantCulture)); sqlCommand = SqlConnection.CreateCommand(sql); sqlCommand.ExecuteNonQuery(); // Iteration Sequence: Bottom-Left (SE) to Top-Right (NW), from lowest Zoom Level to Highest Zoom Level // Iterate through all Zoom Levels (from lowest/zoomed-out to highest/zoomed-in) int zoomLevels = MaxZoom - MinZoom; int zoomLevel = MinZoom; for (int z = 0; z <= zoomLevels; z++) { // Set Zoom zoomLevel = MinZoom + z; System.Diagnostics.Debug.WriteLine("Zoom: " + zoomLevel.ToString()); // Get minimum Tile X, Y values OSMCoordinate MinTMS = GetOSMCoordinates(SELat, SELon, zoomLevel); // Get maximum Tile X, Y values OSMCoordinate MaxTMS = GetOSMCoordinates(NWLat, NWLon, zoomLevel); // Iterate through all Rows (Y-Axis) int numOfRows = MinTMS.Y - MaxTMS.Y; for (int y = 0; y <= numOfRows; y++) { System.Diagnostics.Debug.WriteLine("Row: " + (MinTMS.Y - y).ToString()); // Retrieve tiles from Left to Right (X-Axis) int numOfTiles = MaxTMS.X - MinTMS.X; for (int x = 0; x <= numOfTiles; x++) { // Fetch Tile System.Diagnostics.Debug.WriteLine("X: {0} Y: {1} Zoom: {2}", (MinTMS.X + x).ToString(), (MinTMS.Y - y).ToString(), zoomLevel.ToString()); // Check subdomain to retrieve from if (s > sMax) { s = 0; } Tile tile = new Tile(MinTMS.X + x, MinTMS.Y - y); byte[] tileBytes = FetchTile(tile, SourceTileServer, SourceTileServer.Subdomains[s], zoomLevel); s++; // Insert into database sqlCommand.CommandText = "INSERT INTO tiles (zoom_level, tile_column, tile_row, tile_data) VALUES (@zoom,@x,@y,@image)"; sqlCommand.Bind("@zoom", zoomLevel); sqlCommand.Bind("@x", MinTMS.X + x); sqlCommand.Bind("@y", ((1 << zoomLevel) - (MinTMS.Y - y) - 1)); sqlCommand.Bind("@image", tileBytes); sqlCommand.ExecuteNonQuery(); } } } // Close SQL Connection SqlConnection.Close(); }
/// <summary> /// Creates the URI for a requested Tile /// </summary> /// <param name="Tile">The Slippy Map Tile<see cref="Tile"/></param> /// <returns>The URI that will be used for the web request.</returns> public static Uri GetTileUri(TileServer tileServer, char subdomain, int zoom, int x, int y) { string url = string.Format(tileServer.UriFormat, subdomain, zoom.ToString(), x.ToString(), y.ToString(), tileServer.imgFormat); return(new Uri(url.ToString())); }