OsmSharpを使用してOpenStreetMap(OSM)データを処理する



Use Osmsharp Process Openstreetmap Data



元の住所: OsmSharpを使用してOpenStreetMap(OSM)データを処理する

OsmSharpは、.NETに基づくOpenStreetMap(OSM)ライブラリです。
この記事では、OsmSharpの概要を簡単に説明し、OsmSharpを使用して、OSMデータの一般的なデータ処理、データベースストレージ、およびアプリケーション(パスプランニング)を実行し、いくつかのキーコードを提供します。



OpenStreetMapデータモデル

OSMデータには、ノード、ウェイ、リレーションの3つの基本オブジェクトがあります。詳しくはご確認ください OSM wiki
OpenStreetMap

OsmSharpの紹介

OsmSharpは、直接.NETでOSMデータを使用できます。その主な機能は次のとおりです。
* OSM-XMLの読み取り/書き込み。
* OSM-PBFの読み取り/書き込み。
*ストリーミングアーキテクチャ、最小限のメモリフットプリント。
*ネイティブOSMオブジェクトのストリームを「完全な」OSMオブジェクトに変換します:すべての実際のノードとの方法、インスタンス化されたすべてのメンバーとの関係。
* OSMオブジェクトをジオメトリに変換します。



共同プロジェクト

OsmSharpは、パスプランニング、データ処理、ベクターデータのレンダリングなど、マップに関連する多くのことを行ってきました。以下は、OsmSharpと.NETプラットフォーム間のコラボレーションプロジェクトです。
* NetTopologySuite(NTS) :OsmSharp.Geoで使用してOSMデータをシェープファイルに変換したり、一部のデータをフィルタリングしてGeoJSONに変換したりできる地理ライブラリ。
* ライナー :.NETパスプランニングプロジェクト。 OsmSharp.Routingは、OsmSharpの一部として、ltineroに名前が変更されました。
* Mapsui :Mapsuiは、WPF、Xamarin.Android、Xamarin.iOS、およびUWPアプリケーションの.NETMapコンポーネントです。

ストリーミングモデル

OsmSharpは、ストリーミングモデルを使用してOSMデータを処理します。すべてのフローモデルは、汎用のIEnumerableインターフェイスを実装しています。これは、LINQクエリ処理を使用できることを意味します。
(ストリーミングモデル)
* OsmStreamSource:
XmlOsmStreamSource:OSM-XMLファイルを読み取ります
PBFOsmStreamSource:OSM-PBFファイルを読み取ります
* OsmStreamTarget:
XmlOsmStreamTarget:OSM-XMLファイルを書き込む
PBFOsmStreamTarget:OSM-PBFファイルを書き込みます
* OsmStreamFilter:
OsmStreamFilterDelegate
OsmStreamFilterMerge
OsmStreamFilterNode
OsmStreamFilterProgress

OSM-PBFファイルを読む

luxembourg-latest.osm.pbf



// Read the number of nodes, way and relation of 'luxembourg-latest.osm.pbf' // Create StreamSource var source = new PBFOsmStreamSource(File.OpenRead('luxembourg-latest.osm.pbf')) int nodes = 0, ways = 0, relations = 0 foreach (var osmGeo in source) { if (osmGeo.Type == OsmGeoType.Node) { nodes++ } if (osmGeo.Type == OsmGeoType.Way) { ways++ } if (osmGeo.Type == OsmGeoType.Relation) { relations++ } } Console.WriteLine('There are {0} nodes, {1} ways, {2} relations.', nodes, ways, relations) // Output: There are 1721051 nodes, 223718 ways, 2511 relations.

LINQクエリフィルターデータ

// Create StreamSource var source = new PBFOsmStreamSource(File.OpenRead('luxembourg-latest.osm.pbf')) int nodes = 0, ways = 0, relations = 0 // Use LINQ to query the data named 'Stilmant Michael' var filtered = from osmGeo in source where osmGeo.UserName == 'Stilmant Michael' select osmGeo foreach (var osmGeo in filtered) { if (osmGeo.Type == OsmGeoType.Node) { nodes++ } if (osmGeo.Type == OsmGeoType.Way) { ways++ } if (osmGeo.Type == OsmGeoType.Relation) { relations++ } } Console.WriteLine('There are {0} nodes, {1} ways, {2} relations are edited by Stilmant Michael.', nodes, ways, relations) // Output: There are 762 nodes, 77 ways, 0 relations are edited by Stilmant Michael.

OSM-XMLファイルを書く

// Create StreamSource var source = new PBFOsmStreamSource(File.OpenRead('luxembourg-latest.osm.pbf')) // filter var filterd = from osmGeo in source where osmGeo.UserName == 'Stilmant Michael' select osmGeo // Create StreamTarget var target = new XmlOsmStreamTarget(File.Open('filtered.osm', FileMode.Create)) // write target.RegisterSource(filterd) target.Pull()

次のように、「filtered.osm」ファイルを開いて内容を表示します。

<osm version='0.6' generator='OsmSharp'> <node id='25922353' lat='49.50871' lon='6.010775' user='Stilmant Michael' uid='26290' visible='true' version='36766' changeset='9218254' timestamp='2011-09-05T14:27:47Z'> <tag k='highway' v='crossing' /> <tag k='crossing' v='uncontrolled' /> node> <node id='245921260' lat='49.60019' lon='6.124797' user='Stilmant Michael' uid='26290' visible='true' version='10362' changeset='44411' timestamp='2008-02-05T12:37:48Z' /> <node id='245923096' lat='49.5063' lon='6.013138' user='Stilmant Michael' uid='26290' visible='true' version='10516' changeset='9218254' timestamp='2011-09-05T14:27:46Z'> <tag k='highway' v='crossing' /> <tag k='crossing' v='uncontrolled' /> node> <node id='245923278' lat='49.50129' lon='6.014411' user='Stilmant Michael' uid='26290' visible='true' version='10557' changeset='44411' timestamp='2008-02-05T12:57:49Z' /> <node id='245923344' lat='49.49693' lon='6.009622' user='Stilmant Michael' uid='26290' visible='true' version='10571' changeset='44411' timestamp='2008-02-05T12:59:12Z' /> <node id='245923345' lat='49.49725' lon='6.009501' user='Stilmant Michael' uid='26290' visible='true' version='10572' changeset='44411' timestamp='2008-02-05T12:59:12Z' /> <way id='22921571' user='Stilmant Michael' uid='26290' visible='true' version='1' changeset='87062' timestamp='2008-02-09T16:29:01Z'> <nd ref='246813086' /> <nd ref='246813087' /> <nd ref='246813088' /> <nd ref='246813089' /> <tag k='foot' v='yes' /> <tag k='highway' v='footway' /> <tag k='created_by' v='Potlatch 0.7' /> way> <way id='22921581' user='Stilmant Michael' uid='26290' visible='true' version='2' changeset='87062' timestamp='2008-02-09T16:31:20Z'> <nd ref='246813139' /> <nd ref='246813140' /> <nd ref='246813141' /> <nd ref='246813142' /> <nd ref='246813139' /> <tag k='amenity' v='parking' /> <tag k='created_by' v='Potlatch 0.7' /> way> osm>

一般的なデータ処理

作物

次の2つのフィルタリング方法でクリッピングを実現できます。
* FilterBox(float left、float top、float right、float bottom、bool completeWays)
* FilterSpatial(IPolygonポリゴン、bool completeWays)

その中で、FilterBox()はフレームでフィルタリングされ、左、上、右、下の緯度と経度を設定する必要があります。 FilterSpatial()は、ポリゴン内のすべてのオブジェクトを保持するためにploygonでトリミングされます。このメソッドは、OsmSharp.Geoを参照する必要があります。 completeWaysのデフォルトはfalseです。trueとfalseの違いを次の図に示します。 (赤はtrueに設定された結果、緑はfalseに設定された結果です)
completeWays
例:

using (var fileStreamSource = File.OpenRead('luxembourg-latest.osm.pbf')) { using (var fileStreamTarget = File.Open('clip.osm', FileMode.Create)) { // Create StreamSource var source = new PBFOsmStreamSource(fileStreamSource) // Create StreamTarget var target = new XmlOsmStreamTarget(fileStreamTarget) // 1. Filter by border //var filtered = source.FilterBox(6.238002777099609f, 49.72076145492323f, 6.272850036621093f, 49.69928180928878f) // 2. Filter by polygon var polygon = GetPolygonFromGeoJson('polygon.geojson') var filtered = source.FilterSpatial(polygon, true) target.RegisterSource(filtered) target.Pull() } }

GetPolygonFromGeoJson(string fileName)メソッドは次のとおりです。次の参照を追加する必要があります。

using GeoAPI.Geometries using NetTopologySuite.Features using Newtonsoft.Json /// /// Get Polygon object from GeoJson /// /// file name /// private static IPolygon GetPolygonFromGeoJson(string fileName) { using (var stream = new StreamReader(fileName)) { var jsonSerializer = new NetTopologySuite.IO.GeoJsonSerializer() var features = jsonSerializer.Deserialize(new JsonTextReader(stream)) return features.Features[0].Geometry as IPolygon } }

データベース操作

SQLサーバー

OsmSharp-GitHub SQLServerデータベースとの間でOSMデータを読み書きする方法は2つあります。

OsmSharp.Db.SQLServer.dll

使用する sqlserver-dataprovider ソフトウェアパッケージは、SQLServerをOpenStreetMapデータベースとして使用します。 SQLServerデータベースとの間でOSMデータを読み取り/書き込みします。 'OsmSharp.Db.SQLServer.dll'への参照を追加する必要があります。最新のGitHubでこのファイルが見つかりません。 GitHubにアップロードし、クリックしてダウンロードしました。
OsmSharp.Db.SQLServer.dll

OSMデータをSQLServerデータベースに書き込む

リアルタイムデータ(スナップショット*)と履歴データ(履歴*)の挿入方法は同じですが、データテーブルの構造が異なります。特定のデータテーブル構造を参照してください GitHub のSQLステートメント。

履歴データ(History *)をインポートすると、IDが重複しているため、データのインポートに失敗します。 ! !

// connection string string conStr = 'Server=*Database=*User Id=*Password=*MultipleActiveResultSets=true' // enable logging OsmSharp.Logging.Logger.LogAction = (origin, level, message, parameters) => { Console.WriteLine('[{0}-{3}]:{1} - {2}', origin, level, message, DateTime.Now.ToString()) } // Connect to the database and insert data using (var connection = new SqlConnection(conStr)) { connection.Open() // Create a data table Tools.SnapshotDbCreateAndDetect(connection) // Delete the data table content Tools.SnapshotDbDeleteAll(connection) using (var stream = File.OpenRead('luxembourg-latest.osm.pbf')) { var source = new PBFOsmStreamSource(stream) var target = new SnapshotDbStreamTarget(conStr, true) // Insert data target.RegisterSource(source) target.Pull() } }

出力ログ:

[Schema.Tools-2017/5/12 13:32:47]:information - Delete all data in snapshot database schema... [SnapshotDbStreamTarget-2017/5/12 13:32:53]:information - Inserting 1000000 records into node. [SnapshotDbStreamTarget-2017/5/12 13:33:14]:information - Inserting 80777 records into node_tags. [SnapshotDbStreamTarget-2017/5/12 13:33:22]:information - Inserting 100000 records into way. [SnapshotDbStreamTarget-2017/5/12 13:33:23]:information - Inserting 354601 records into way_tags. [SnapshotDbStreamTarget-2017/5/12 13:33:28]:information - Inserting 1122157 records into way_nodes. [SnapshotDbStreamTarget-2017/5/12 13:33:43]:information - Inserting 100000 records into way. [SnapshotDbStreamTarget-2017/5/12 13:33:44]:information - Inserting 256525 records into way_tags. [SnapshotDbStreamTarget-2017/5/12 13:33:47]:information - Inserting 888866 records into way_nodes. [SnapshotDbStreamTarget-2017/5/12 13:33:59]:information - Flushing remaining data... [SnapshotDbStreamTarget-2017/5/12 13:33:59]:information - Inserting 721051 records into node. [SnapshotDbStreamTarget-2017/5/12 13:34:15]:information - Inserting 55585 records into node_tags. [SnapshotDbStreamTarget-2017/5/12 13:34:16]:information - Inserting 23718 records into way. [SnapshotDbStreamTarget-2017/5/12 13:34:16]:information - Inserting 52142 records into way_tags. [SnapshotDbStreamTarget-2017/5/12 13:34:16]:information - Inserting 282298 records into way_nodes. [SnapshotDbStreamTarget-2017/5/12 13:34:19]:information - Inserting 2511 records into relation. [SnapshotDbStreamTarget-2017/5/12 13:34:20]:information - Inserting 14759 records into relation_tags. [SnapshotDbStreamTarget-2017/5/12 13:34:20]:information - Inserting 146095 records into relation_members. [SnapshotDbStreamTarget-2017/5/12 13:34:22]:information - Database connection closed.

リアルタイムデータと挿入データのテーブル構造は以下のとおりです。
画像

SQLServerデータベースからOSMデータを読み取る
// connection string string conStr = 'Server=*Database=*User Id=*Password=*MultipleActiveResultSets=true' using (var connection = new SqlConnection(conStr)) { connection.Open() // Read OSM data from the database var source = new SnapshotDbStreamSource(conStr) int nodes = 0, ways = 0, relations = 0 foreach (var osmGeo in source) { if (osmGeo.Type == OsmGeoType.Node) { nodes++ } if (osmGeo.Type == OsmGeoType.Way) { ways++ } if (osmGeo.Type == OsmGeoType.Relation) { relations++ } } Console.WriteLine('There are {0} nodes, {1} ways, {2} relations.', nodes, ways, relations) } // Output: There are 1721051 nodes, 223718 ways, 2511 relations.

OsmSharp.Data.SQLServer.dll

使用する データプロバイダー ソフトウェアパッケージでは、「OsmSharp.Data.SQLServer.dll」ファイルをダウンロードして参照を追加できます。また、NuGetを使用して「OsmSharp.SQLServer」を検索してインストールすることもできます。

NuGetを使用してインストールすることをお勧めします。これにより、対応するバージョンのOsmSharpライブラリが自動的に追加されます。そうしないと、問題が発生する可能性があります。以下は、この記事に対応するバージョン番号です。

<packages> <package id='OsmSharp' version='4.2.0.723' targetFramework='net452' /> <package id='OsmSharp.SQLServer' version='4.2.0.723' targetFramework='net452' /> packages>
SQLServerデータベースはOSMデータを書き込みます

history.pbf
OsmSharp.Data.SQLServer.dllはIDを制限しないため、IDが重複する履歴データをデータベースに直接書き込むこともできます。

string conStr = 'Server=*Database=*User Id=*Password=*MultipleActiveResultSets=true' using (var connection = new SqlConnection(conStr)) { connection.Open() // Delete the original data table SQLServerSchemaTools.Remove(connection) // Create a data table SQLServerSchemaTools.CreateAndDetect(connection) // Real-time data // using (Stream stream = File.OpenRead('luxembourg-latest.osm.pbf')) // historical data using (Stream stream = File.OpenRead('history.pbf')) { var source = new PBFOsmStreamSource(stream) var target = new SQLServerOsmStreamTarget(connection) // Insert data target.RegisterSource(source) target.Pull() } }

次の図は、履歴データを書き込んだ結果です。IDが重複していることがわかります。
画像

OSMデータを読み取るためのSQLServerデータベース

OsmSharp.Data.SQLServer.dllは、SQLServerデータベースからOSMデータを読み取ることができないようです。

ルートプラン

旅程

ライナー :.NETパスプランニングプロジェクト。
* Itinerary.IO.OSM:OSMデータを使用する
* Itinero.IO.Shape:シェープファイルを使用
* Route.IO.Geo:NTSを使用

主なカテゴリ: RouterDbルータープロファイル そしてRouterPoint
* RouterDb: 1つのルーティングネットワークのデータを管理します。すべてのデータをRAMに保持するか、メモリマッピング戦略を使用してオンデマンドでデータをロードします。これは、すべてのネットワークジオメトリ、メタデータ、およびトポロジを保持します。
* ルーター: 利用可能なすべてのルーティング機能のメインファサード。要求されたものとRouterDbで利用可能なデータの組み合わせに基づいて、使用するのに最適なアルゴリズムを決定します。
* プロファイル: ルーティングネットワークを通過できる車両の定義とその動作。
* RouterPoint :ルートの始点または終点として使用するルーティングネットワーク上の場所。これは、ネットワーク上の場所を一意に識別するエッジIDとオフセット値によって定義されます。

以下では、簡単な例を使用して、パス計画にOSMデータを使用する方法について説明します。

NuGetを介してItineroとItinero.IO.OSMをインストールし、参照を追加します

using Itinero using Itinero.IO.Osm using Itinero.LocalGeo using Itinero.Osm.Vehicles

OSMデータをロードする

// Load OSM data var routerDb = new RouterDb() using (var stream = File.OpenRead('beijing.osm.pbf')) { routerDb.LoadOsmData(stream, Vehicle.Car) }

ルーターの作成

var router = new Router(routerDb)

計算

var route = router.Calculate(Vehicle.Car.Fastest(), new Coordinate(39.9225f, 116.3669f), new Coordinate(39.9066f, 116.4053f)) // Vehicle // public static readonly Profiles.Vehicle Car // public static readonly Profiles.Vehicle Pedestrian // public static readonly Profiles.Vehicle Bicycle // public static readonly Profiles.Vehicle Moped // public static readonly Profiles.Vehicle MotorCycle // public static readonly Profiles.Vehicle SmallTruck // public static readonly Profiles.Vehicle BigTruck // public static readonly Profiles.Vehicle Bus

結果

// route.TotalDistance // distance // route.TotalTime // time // route.ToGeoJson() // Convert to GeoJson format: string // route.WriteGeoJson()// Write GeoJson file // ......

結果をGeoJsonファイルに書き込んだ後、に追加します。 geojson.io 、計算されたパスは次のとおりです

GeoJson

実用化

上記の例は、小さなエリア、都市、または国では問題ありませんが、大きなエリアのデータをロードする場合は、次の方法を使用してください。
1.元のデータを処理し、ディスクに書き込みます。
2.前処理されたデータをディスクからロードし、パスプランニングに使用します。

生データをロードする

var routerDb = new RouterDb() using (var stream = File.OpenRead('beijing.osm.pbf')) { routerDb.LoadOsmData(stream, Vehicle.Car) }

routerDbインスタンスをディスクに書き込みます。

using (var stream = File.Create('osmfile.routing')) { routerDb.Serialize(stream) }

routerDbをロードします

using (var stream = File.OpenRead('osmfile.routing')) { var routerDb = RouterDb.Deserialize(stream, RouterDbProfile.NoCache) }

計算

var router = new Router(routerDb) var profile = Vehicle.Car.Fastest() var routerPoint1 = router.TryResolve(profile, 39.9225f, 116.3669f) if(routerPoint1.IsError) { // do something or retry. } var routerPoint2 = router.TryResolve(profile, 39.9066f, 116.4053f) if (routerPoint2.IsError) { // do something or retry. } var route = router.TryCalculate(Vehicle.Car.Fastest(), routerPoint1.Value, routerPoint2.Value) if(route.IsError) { // do something or retry. }

パスプランニングの詳細については、を参照してください。 ルート-GitHub