c# - 在 C# 中,osm 文件(osm.pbf)的处理和过滤花费的时间太长

标签 c# .net openstreetmap large-data large-files

场景:我想编写自己的地址自动完成 API,就像 Google 提供的 One 一样。 (非常基本:街道、门牌号、城市、邮政编码、国家/地区)。它仅供私有(private)使用和培训目的。我想首先覆盖大约 100 万个地址。

使用的技术: .Net Framework(非 Core)、C#、Visual Studio、OSMSharp、Microsoft SQL-Server、Web Api 2(尽管我将来可能会切换到 ASP.Net Core) .)

方法:

  • 设置项目(用于演示目的的 Web Api 2 或控制台项目)
  • 使用 DownloadClient() 从 OpenStreetMaps 下载相关文件 ( https://download.geofabrik.de/ )
  • 使用 OSMSharp 读入文件并过滤掉相关数据。
  • 将过滤后的数据转换为数据表。
  • 使用DataTable提供SQLBulkCopy方法将数据导入数据库。

问题:第 4 步花费的时间太长。对于像“Regierungsbezirk Köln”这样格式为 osm.pbf 的文件,该文件大约为 160MB(未压缩的 osm 文件大约为 2.8 GB),大约需要 4-5 小时。我想优化这个。另一方面,将数据表批量复制到数据库(大约 100 万行)只需大约 5 秒。 (哇。太棒了。)

最小复制: https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction

我尝试过的:

  • 在 SQL-Server 中使用存储过程。这带来了一系列完全不同的问题,我没能完全让它工作(主要是因为未压缩的 osm.pbf 文件超过 2GB,而 SQL Server 不喜欢这样)

  • 想出一种不同的方法来过滤文件中的数据并将其转换为数据表(或 CSV)。

  • 使用 Overpass-API。尽管我在某处读到 Overpass-API 不适用于超过 10,000 个条目的数据集。

  • 向 StackOverflow 上的绝地大师寻求帮助。 (目前正在进行中...:D)

代码摘录:

public static DataTable getDataTable_fromOSMFile(string FileDownloadPath)
{

    Console.WriteLine("Finished Downloading. Reading File into Stream...");

    using (var fileStream = new FileInfo(FileDownloadPath).OpenRead())
    {
        PBFOsmStreamSource source = new PBFOsmStreamSource(fileStream);

        if (source.Any() == false)
        {
            return new DataTable();
        }

        Console.WriteLine("Finished Reading File into Stream. Filtering and Formatting RawData to Addresses...");
        Console.WriteLine();

        DataTable dataTable = convertAdressList_toDataTable(
                    source.Where(x => x.Type == OsmGeoType.Way && x.Tags.Count > 0 && x.Tags.ContainsKey("addr:street"))
                    .Select(Address.fromOSMGeo)
                    .Distinct(new AddressComparer())
                );

        return dataTable;
    }
};
private static DataTable convertAdressList_toDataTable(IEnumerable<Address> addresses)
{
    DataTable dataTable = new DataTable();

    if (addresses.Any() == false)
    {
        return dataTable;
    }

    dataTable.Columns.Add("Id");
    dataTable.Columns.Add("Street");
    dataTable.Columns.Add("Housenumber");
    dataTable.Columns.Add("City");
    dataTable.Columns.Add("Postcode");
    dataTable.Columns.Add("Country");

    Int32 counter = 0;

    Console.WriteLine("Finished Filtering and Formatting. Writing Addresses From Stream to a DataTable Class for the Database-SQLBulkCopy-Process ");

    foreach (Address address in addresses)
    {
        dataTable.Rows.Add(counter + 1, address.Street, address.Housenumber, address.City, address.Postcode, address.Country);
        counter++;

        if (counter % 10000 == 0 && counter != 0)
        {
            Console.WriteLine("Wrote " + counter + " Rows From Stream to DataTable.");
        }
    }

    return dataTable;
};

最佳答案

好吧,我想我明白了。过滤后,大约 600mb 的文件大小和大约 310 万行数据的时间减少到大约 12 分钟。

我尝试的第一件事是用 FastMember 替换填充我的 DataTable 的逻辑。这有效,但没有提供我所希望的性能提升(我在 3 小时后取消了该过程......)。经过更多研究后,我偶然发现了一个名为“osm2mssql”的旧项目(https://archive.codeplex.com/?p=osm2mssql)。我使用了一小部分代码,直接从 osm.pbf 文件中读取数据并将其修改为我的用例(→ 从 Ways 中提取地址数据)。我实际上确实使用 FastMember 编写了 IEnumerable<Address>到数据表,但我不需要 OSM-Sharp 以及它们不再有的任何额外依赖项。非常感谢FastMember的建议。我一定会在未来的项目中牢记该库。

对于那些感兴趣的人,我相应地更新了我的 Github 项目 ( https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction )(尽管我没有彻底测试它,因为我从测试项目转到了真正的交易,这是一个 Web Api)

我很确定它可以进一步优化,但我认为我现在不在乎。我认为 12 分钟的方法可能每月调用一次来更新整个数据库就可以了。现在我可以继续优化自动完成查询。

非常感谢编写“osm2mssql”的人。

关于c# - 在 C# 中,osm 文件(osm.pbf)的处理和过滤花费的时间太长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59680569/

相关文章:

c# - 将 NuGet 包替换为本地 dll 文件

c# - List<T> 到 T[] 而不复制

c# - 从外部应用程序向 Web 浏览器添加书签

c# - 从 LINQ to SQL 获取 SQL 查询?

linux - 好吧,我只想了解从包中构建 tile 服务器的确切步骤

c# - 带有 OpacityMask 的 WPF DataTrigger

c# - C# 如何处理调用结构上的接口(interface)方法?

.net - 使用事件目录中的信息解锁窗口

java - JMapViewer 添加图 block 以供离线查看

python - 如何在 Python 中计算多边形的质心