5

I have web application that uses Leaflet to show map of a small region. The problem is that the application must work offline(on LAN) and map tiles are loaded from Openstreetmap. I downloaded the tiles that I needed but I haven't find a good documentation about how to use the downloaded file(which is a 500MB file with .mbtiles extension). Here is the default approach suggested by Leaflet :

    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
    }).addTo(map); 

How do I set up a server using dotnet core to use my offline tiles and given parameters and get tiles images?(like the following):

L.tileLayer('https://example.com/DotnetCore/WepApi/GetTiles?{s}/{z}/{x}/{y}').addTo(map);
nAviD
  • 2,784
  • 1
  • 33
  • 54
  • 1
    I don't know the OSM file format `tiles`, but I think, it must be some kind of container. Did you try to open the `tiles` file with anything like 7-Zip? If yes: does it contains the cache tiles for the different LODs? Or is there any documentation? – mu88 Apr 08 '19 at 08:56
  • I found that .MBTiles is actually Sqlite database file . – nAviD Apr 08 '19 at 21:28

1 Answers1

4

Here is a basic implementation to read the tiles file and serve them in your WebApi.

You need to install the NuGet System.Data.SQLite.Core (or similar) to access the database.

Helper class:

public class MbTilesReader
{
    private string _mbTilesFilename;

    public MbTilesReader(string mbTilesFilename)
    {
        _mbTilesFilename = mbTilesFilename;
    }

    public byte[] GetImageData(int x, int y, int zoom)
    {
        byte[] imageData = null;
        using (SQLiteConnection conn = new SQLiteConnection(string.Format("Data Source={0};Version=3;", _mbTilesFilename)))
        {
            conn.Open();
            using (SQLiteCommand cmd = new SQLiteCommand(conn))
            {
                cmd.CommandText = "SELECT * FROM tiles WHERE tile_column = @x and tile_row = @y and zoom_level = @z";
                cmd.CommandType = System.Data.CommandType.Text;
                cmd.Parameters.Add(new SQLiteParameter("@x", x));
                cmd.Parameters.Add(new SQLiteParameter("@y", y));
                cmd.Parameters.Add(new SQLiteParameter("@z", zoom));
                SQLiteDataReader reader = cmd.ExecuteReader();
                if (reader.Read())
                {
                    imageData = reader["tile_data"] as byte[];
                }
            }
        }
        return imageData;
    }
}

Then register the class as a singleton in your ConfigureServices method and pass the path to the file:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(new MbTilesReader("c:/temp/map.mbtiles"));
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Finally, you can return the image in your WebApi action as follows:

[Route("api/[controller]")]
[ApiController]
public class MapController : ControllerBase
{
    private MbTilesReader _tileReader;

    public MapController(MbTilesReader tileReader)
    {
        _tileReader = tileReader;

    }

    [HttpGet]
    public IActionResult Get(int x, int y, int z)
    {
        byte[] imageData = _tileReader.GetImageData(x, y, z);
        return File(imageData, "image/png");
    }
}

Possible improvements

  • Use a cache to avoid querying all the time for the same images.
  • Make the implementation async (see this question).

Edit - Formats

This answer assumes your data is stored in PNG format, .mbtiles files can store data in the following formats pbf (for vectors), jpg, png, and webapp. To know which format your database is using, check the data in the table metadata of the .mbtiles SQLite database.

See the following link for more info: https://github.com/mapbox/mbtiles-spec/blob/master/1.3/spec.md

Isma
  • 14,604
  • 5
  • 37
  • 51
  • I tested this now it seems that the byte array is not in image format even it can not be saved by Image class. – nAviD Apr 27 '19 at 19:35
  • How are you saving it? – Isma Apr 27 '19 at 23:24
  • First I tried different ways of saving files like using MemoryStream and Image.FromMemoryStream.Then when I was sure of saving it correctly I used online tools to check the file format and found that it is .gz which is a compressed file. Then I renamed and extracted the file and this time the result was ApplicationOctedStream. Every piece of code I found on the internet used the tile_data as Image. – nAviD Apr 28 '19 at 15:41
  • any other comment? – nAviD May 05 '19 at 12:18
  • Actually, you mention your mbtiles are in pbf format, I think this is intended for vector data, are you sure you are downloading the images in png format? Can you send me the URL where you generated the file from? I made a little update to the answer. – Isma May 05 '19 at 16:11