Сценарий: Я хочу написать свой собственный Autocomplete-API для адресов, как и тот, который предлагает Google. (Very Basi c: улица, номер дома, город, почтовый индекс, страна). Он предназначен только для личного использования и в учебных целях. Я хочу покрыть около 1 миллиона адресов для начала.
Используемая технология: . Net Framework (не Core), C#, Visual Studio, OSMSharp, Microsoft SQL -Server, Web Api 2 (хотя я, вероятно, переключусь на ASP. Net Core в будущем.)
Подход:
- Set Up Project (Web Api 2 или консольный проект для демонстрационных целей)
- Загрузить соответствующий файл из OpenStreetMaps с помощью DownloadClient () (https://download.geofabrik.de/)
- Читать в файле использование OSMSharp и фильтрация соответствующих данных.
- Преобразование отфильтрованных данных в таблицу данных.
- Использование DataTable для подачи метода SQLBulkCopy для импорта данных в базу данных.
Проблема: Шаг 4 занимает слишком много времени. Для файла типа "Regierungsbezirk Köln" в формате osm.pbf, который составляет около 160 МБ (несжатый файл osm составляет около 2,8 ГБ), где речь идет о 4-5 часах. Я хочу оптимизировать это. С другой стороны, массовое копирование DataTable в базу данных (около 1 миллиона строк) занимает всего около 5 секунд. (Вау. Удивительно.)
Минимальное воспроизведение: https://github.com/Cr3pit0/OSM2Database-Minimal-Reproduction
Что я пробовал:
Использовать хранимую процедуру в SQL -Server. Это связано с совершенно другим набором проблем, и мне не удалось заставить его работать (в основном потому, что размер несжатого файла osm.pbf превышает 2 ГБ, а SQL серверу это не нравится)
Придумайте другой подход к фильтрации и преобразованию данных из файла в таблицу данных (или CSV).
Используйте Overpass-API. Хотя я где-то читал, что Overpass-API не предназначен для DataSets выше 10000 записей.
Обратитесь за помощью к гроссмейстерам-джедаям в 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;
};