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

Posted

技术标签:

【中文标题】osm-File(osm.pbf) 的处理和过滤在 C# 中花费的时间太长【英文标题】:Processing and Filtering of osm-File(osm.pbf) is taking way too long in C# 【发布时间】:2020-04-28 00:55:10 【问题描述】:

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

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

方法:

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

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

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

我尝试了什么:

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

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

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

向 *** 上的绝地大师寻求帮助。 (目前正在处理中......: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;
;

【问题讨论】:

您是否已经尝试使用分析器来确定您的瓶颈? 您需要向我们展示如何生成DataTable 的代码,因为那是您的瓶颈所在。看到这个答案:***.com/a/564373/3181 所有相关代码都在github的Project中。如果我只是将数十行代码复制并粘贴到这个问题中,我不知道它是否会受到赞赏。至于瓶颈。我使用包含 DateTimes 的简单日志来确定哪个操作花费的时间最长。 @StuartGrassie FastMember 听起来很有趣,尽管我不确定这是否是我需要的。有时间我会去看看的。 正如@StuartGrassie 所说,您的DataTable 填充代码在逐行填充时效率可怕地。 FastMember 或类似产品是您最好的选择。 【参考方案1】:

好的,我想我明白了。对于大约 600mb 的文件大小和过滤后的大约 310 万行数据,我可以缩短到大约 12 分钟。

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

对于那些感兴趣的人,我相应地更新了我的 Github-Project (https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction)(虽然我没有彻底测试它,因为我从 Test-Project 转到了 Real Deal,这是一个 Web Api)

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

非常感谢写“osm2mssql”的人。

【讨论】:

以上是关于osm-File(osm.pbf) 的处理和过滤在 C# 中花费的时间太长的主要内容,如果未能解决你的问题,请参考以下文章

ruby 使用从osm.pbf输入中提取的多边形列表编辑输入osm文件的过程

ruby 使用从osm.pbf输入中提取的多边形列表编辑输入osm文件的过程

ruby 使用从osm.pbf输入中提取的多边形列表编辑输入osm文件的过程

将 shp 文件(sf 对象)转换为 osm pbf?

使用Postgis使用SQL查询在Postgres中使用OSM给出(lat,long)时检索最近的道路

如何通过用户界面中的插槽处理文件路径?