如何在 .NET Core 中使用离线 OpenStreetMap(OSM)瓦片文件?

5

我有一个Web应用程序,使用Leaflet来显示一个小区域的地图。问题在于该应用程序必须脱机(在局域网上)工作,并且地图瓦片是从Openstreetmap加载的。我下载了需要的瓦片,但我没有找到关于如何使用已下载文件的好文档(它是一个500MB带有.mbtiles扩展名的文件)。这里是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); 

如何使用 dotnet core 设置服务器,以使用我的离线切片和给定参数,并获取切片图像(如下所示)?

L.tileLayer('https://example.com/DotnetCore/WepApi/GetTiles?{s}/{z}/{x}/{y}').addTo(map);

1
我不知道OSM文件格式中的“tiles”,但我认为它必须是某种容器。您是否尝试使用类似于7-Zip的工具打开“tiles”文件?如果是,请问它是否包含了不同LOD级别的缓存瓦片?或者有没有任何文档资料可供参考? - mu88
我发现 .MBTiles 实际上是 Sqlite 数据库文件。 - nAviD
1个回答

4

这是一个基本的实现,用于读取瓦片文件并在您的WebApi中提供服务。

您需要安装NuGet System.Data.SQLite.Core(或类似的)以访问数据库。

辅助类:

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;
    }
}

然后在ConfigureServices方法中将该类注册为单例并传递文件路径:

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

最后,在您的WebApi操作中,您可以按照以下方式返回图像:
[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");
    }
}

可能的改进

  • 使用缓存避免重复查询相同的图片。
  • 将实现异步 ( 参见问题 )。

编辑 - 格式

本答案假定您的数据以PNG格式存储,.mbtiles文件可以存储以下格式的数据:pbf(用于矢量图)、jpg、png和webapp。要了解数据库使用哪种格式,请检查.mbTiles SQLite数据库的表元数据中的数据。

有关更多信息,请参阅以下链接:https://github.com/mapbox/mbtiles-spec/blob/master/1.3/spec.md


我现在进行了测试,似乎这个字节数组不是图像格式,即使使用Image类也无法保存。 - nAviD
你是如何保存它的? - Isma
首先,我尝试了不同的文件保存方式,比如使用MemoryStream和Image.FromMemoryStream。当我确信保存正确后,我使用在线工具检查文件格式,发现它是压缩文件.gz。然后我重命名并提取文件,这次结果是ApplicationOctedStream。我在互联网上找到的每一段代码都将tile_data作为图像。 - nAviD
还有其他评论吗? - nAviD
实际上,您提到您的mbtiles是pbf格式,我认为这是用于矢量数据的,您确定您正在以png格式下载图像吗?您能否向我发送生成文件的URL?我对答案进行了小修改。 - Isma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接