0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Automating GIS Processes 2024 写経 Lesson 6 Network analysis in Python(2)

Posted at

前回の続きです。

Shortest path analysis

Let’s now calculate the shortest path between two points using osmnx.shortest_path() <https://osmnx.readthedocs.io/en/stable/osmnx.html?highlight=get_nearest_node#osmnx.distance.shortest_path>__.

では、osmnx.shortest_path()を使って、2つの座標の最短経路を計算しましょう。

Origin and destination points

First we need to specify the source and target locations for our route. If you are familiar with the Kamppi area, you can specify a custom placename as a source location. Or, you can follow along and choose these points as the origin and destination in the analysis:

  • "Maria 01, Helsinki" <https://nominatim.openstreetmap.org/ui/search.html?q=Maria+01>__: a startup hub in a former hospital area.
  • "ruttopuisto" <https://nominatim.openstreetmap.org/ui/search.html?q=ruttopuisto>__, a park. The park’s official name is ’Vanha kirkkopuisto’, but Nominatim is also able to geocode the nickname.

We could figure out the coordinates for these locations manually, and create shapely.geometry.Points based on the coordinates. However, if we would have more than just two points, that would quickly become a chore. Instead, we can use OSMnx to geocode the locations.

Remember to transform the origin and destination points to the same reference system as the network data.

最初に、ルートの始点と終点を特定する必要があります。もしKamppiエリアになじみがあるのならば、個別の地名を始点として特定できます。もしくは、これらの座標を始点と終点を、分析用に選択することもできます。

  • "Maria 01, Helsinki": 以前は病院だったエリアにあるスタートアップの集積地
    image.png

  • "ruttopuisto": とある公園。この公園の公式の名前は’Vanha kirkkopuisto’だが、Nominatimはニックネームもジオコーディングできる
    image.png

これらの場所の座標を手動で特定し、座標に基づきshapely.geometry.Pointを作れるかもしれません。しかしながら、座標が2つより多くになってしまったら、すぐに面倒になるかもしれません。代わりに、OSMnxを使って、座標をジオコーディングできます。

origin = (
    osmnx.geocode_to_gdf("Maria 01, Helsinki")  # fetch geolocation
    .to_crs(edges.crs)  # transform to UTM
    .at[0, "geometry"]  # pick geometry of first row
    .centroid  # use the centre point
)

destination = (
    osmnx.geocode_to_gdf("ruttopuisto")
    .to_crs(edges.crs)
    .at[0, "geometry"]
    .centroid
)

image.png

We now have shapely.geometry.Points representing the origin and destination locations for our network analysis. In a next step, we need find these points on the routable network before the final routing.

ネットワーク分析に必要な、始点と終点の場所を示すshapely.geometry.Pointの座標を取得しました。次のステップとして、最終のルーティングの前に、ルーティング可能なネットワーク上でこれらの座標を見つける必要があります。

Nearest node

To route on the network, we first have to find a starting point and endpoint that is part of the network. Use osmnx.distance.nearest_nodes() to return the nearest node’s ID:

ネットワーク上でルートを進むために、最初に、ネットワークの部品である始点と終点を見つける必要があります。最も近いノードのIDを返す、osmnx.distance.nearest_nodes()を使います。

origin_node_id = osmnx.nearest_nodes(graph, origin.x, origin.y)
print(origin_node_id)

destination_node_id = osmnx.nearest_nodes(graph, destination.x, destination.y)
print(destination_node_id)

image.png

Routing

Now we are ready for routing and to find the shortest path between the origin and target locations. We will use osmnx.shortest_path() <https://osmnx.readthedocs.io/en/stable/osmnx.html?highlight=get_nearest_node#osmnx.distance.shortest_path>__.

The function accepts three mandatory parameters: a graph, an origin node id, and a destination node id, and two optional parameters: weight can be set to consider a different cost impedance than the length of the route, and cpus controls parallel computation of many routes.

始点から終点へのルーティングと最短経路を見つける準備ができました。osmnx.shortest_path()を使います。
この関数には3つの必須のパラメータがあります。グラフ・始点のノードID・終点のノードです。そして、2つのオプションのパラメータがあります。weightはルートの長さとは異なる_コストのインピーダンス(この場合は「重み」でしょうか)を考慮するために設定され、cpusにより、多くのルートを並列で計算ことを制御します。

# Find the shortest path between origin and destination
route = osmnx.shortest_path(G=graph, orig=origin_node_id, dest=destination_node_id)
route

image.png

As a result we get a list of all the nodes that are along the shortest path.

We could extract the locations of those nodes from the nodes GeoDataFrame and create a LineString presentation of the points, but luckily, OSMnx can do that for us and we can plot shortest path by using plot_graph_route() function:

結果として、私たちは最短経路に沿った全ノードのリストを取得します。
ノードのGeoDataFrameから、ノードの場所を抽出し、座標群を表現するLineStringを作ることができます。
ただ、ありがたいことに、OSMnxのplot_graph_route()関数は、この処理を実施し、最短経路をプロットしてくれます。

graph前回で求めたものです。

# Plot the shortest path
fig, ax = osmnx.plot_graph_route(G=graph, route=route)

image.png

Nice! Now we have the shortest path between our origin and target locations. Being able to analyze shortest paths between locations can be valuable information for many applications. Here, we only analyzed the shortest paths based on distance but quite often it is more useful to find the optimal routes between locations based on the travelled time. Here, for example we could calculate the time that it takes to cross each road segment by dividing the length of the road segment with the speed limit and calculate the optimal routes by taking into account the speed limits as well that might alter the result especially on longer trips than here.

いいですね!

今私たちは始点と終点の最短経路を取得しています。最短経路の分析ができることは多くのアプリケーションにとって価値があることになりえます。

ここでは、私たちは単純に距離に基づいて最短経路を分析しました。しかし、移動時間に基づき、最適な経路を見つけることのほうがより有益です。

例えば、各道路区間の長さを制限速度で割ることで、通過にかかる所要時間を算出し、制限速度を考慮した最適なルートを求めることができます。特に移動距離が長い場合は、ルートの結果が変わるかもしれません。

Saving shortest paths to disk

Quite often you need to save the route into a file for further analysis and visualization purposes, or at least have it as a GeoDataFrame object in Python. Hence, let’s continue still a bit and see how we can turn the route into a linestring and save the shortest path geometry and related attributes into a geopackage file.

First we need to get the nodes that belong to the shortest path:

さらなる分析や可視化の目的のために、もしくは、少なくともPythonのGeoDataFrameのオブジェクトとして保持するため、頻繁にルートをファイルに保存する必要があります。

したがって、もう少し続けて、どのように私たちがルートをLinestringに変換するのか、最短経路のgeometryや関連する属性をgeopackageのファイルに保存するのかを見ましょう。

最初に、最短経路に属するノードを取得する必要があります:

# Get the nodes along the shortest path
route_nodes = nodes.loc[route]
route_nodes.head()

image.png

ノードは49本あるようです。
image.png

As we can see, now we have all the nodes that were part of the shortest path as a GeoDataFrame.

Now we can create a LineString out of the Point geometries of the nodes:

ご覧の通り、私たちは、最短経路の部品である全ノードを、route_nodesに、GeoDataFrameとして取得しています。

そして、全ノードのPointから、LineStringを作成できます。

import shapely.geometry

# Create a geometry for the shortest path
route_line = shapely.geometry.LineString(
    list(route_nodes.geometry.values)
)
route_line

image.png

Now we have the route as a LineString geometry.

Let’s make a GeoDataFrame out of it having some useful information about our route such as a list of the osmids that are part of the route and the length of the route.

ルートをLineStringとして取得しています。

そこから、ルートの一部であるosmidsのリストや、ルートの長さなど、ルートに関するいくつかの有益な情報を持つGeoDataFrameを作りましょう。

import geopandas

route_geom = geopandas.GeoDataFrame(
    {
        "geometry": [route_line],
        "osm_nodes": [route],
    },
    crs=edges.crs
)

# Calculate the route length
route_geom["length_m"] = route_geom.length

route_geom.head()

image.png

Now we have a GeoDataFrame that we can save to disk. Let’s still confirm that everything is ok by plotting our route on top of our street network and some buildings, and plot also the origin and target points on top of our map.

Download buildings:

道路ネットワークやいくつかの建物の上に、私たちのルートのGeoDataFrame`や始点と終点をプロットすることで、すべてがOKかどうかを確認しましょう。

buildings = osmnx.features_from_place(
    PLACE_NAME,
    {
        "building" : True
    }
).to_crs(edges.crs)

buildings.head()

image.png

Let’s now plot the route and the street network elements to verify that everything is as it should:

すべてが妥当であることを確認するために、ルートとストリートネットワークの要素をプロットしましょう。

まずは、contextilyが未インストールでエラーになるので、pip installでインストールします。
image.png

! pip install contextily

image.png

改めて、ルートとストリートネットワークの要素をプロットします。

import contextily
import matplotlib.pyplot

fig, ax = matplotlib.pyplot.subplots(figsize=(12,8))

# Plot edges and nodes
edges.plot(ax=ax, linewidth=0.75, color='gray')
nodes.plot(ax=ax, markersize=2, color='gray')

# Add buildings
ax = buildings.plot(ax=ax, facecolor='lightgray', alpha=0.7)

# Add the route
ax = route_geom.plot(ax=ax, linewidth=2, linestyle='--', color='red')

# Add basemap
contextily.add_basemap(ax, crs=buildings.crs, source=contextily.providers.CartoDB.Positron)

image.png

Great everything seems to be in order! As you can see, now we have a full control of all the elements of our map and we can use all the aesthetic properties that matplotlib provides to modify how our map will look like. Now we are almost ready to save our data into disk.

すばらしい。すべてが適切に見えます。ご覧の通り、マップのすべての要素を完全にコントロールし、地図の見栄えをよくするためにmatplotlibが提供しているすべての美しい属性を使うことができています。
よって、データをファイル保存する準備ができました。

長くなったのでここまでにします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?