Shapely の Buffer について（基本）

# この記事について

仕事で GIS データを扱うことになったので、基本から学ぶことにしました。
以下の講座を受けながら、Buffer の概念についてメモ。

https://www.udemy.com/course/python_gis/

# Buffer

## Buffer とは

点や線に一定の幅を与えて、Polygonにする操作。
ある地点(`Point`)から半径○km以内の店を探す、とか、ある道路（`LineString`）の沿線○m以内の店を探す、といったときに便利。

PolygonにBufferを与えて、ある地域(`Polygon`)の中と周辺○km以内の店を探す、みたいな使い方をすることもできる。

## Buffer の使い方（基本）

以下のコードは、下記のライブラリがインポートされていることが前提です。

```python: ライブラリのインポート
import geopandas as gpd
from shapely.geometry import Point, LineString, Polygon
```


たとえば、以下のような `GeoSeries` があったとします。

```python:点、線、図形をまとめて作る
s = gpd.GeoSeries(
    [
        Point(0,0),
        LineString([(0,0),(1,1),(3,0),(5,2)]),
        Polygon([(-1,-1),(-2,-2),(1,-2),(-1,-1)])
    ]

)
```

- `s[0]` には点(`Point`オブジェクト)
- `s[1]` には線(`LineString`オブジェクト)
- `s[2]` には図形(`Polygon`オブジェクト)

が、それぞれ入った状態です。

こんな感じ。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/4e5205bd-b3ee-cdf3-c685-5b8be3b911ad.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/17507257-352f-a7cb-386b-21efe7915dbc.png)



### Buffer を与えるための文法

Buffer を与えるための文法は以下のとおりです。

```python: Buffer を与えるための文法
ジオメトリインスタンス.buffer(バッファの大きさ)
```


### 点(`Point`オブジェクト) に Buffer を与える

```python: 点（Point オブジェクト）に Buffer を与える
s[0].buffer(0.5)
```

こうすると以下のように、`s[0]` から半径 `0.5` の円が描画できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/cb2580c9-893b-ef82-c4de-b7993a5232c1.png)

Buffer を与えた後の `Point` オブジェクトの情報を表示すると…

```python: Buffer を与えた後の Point オブジェクトの情報を表示
print(s[0].buffer(0.5))
```

以下のように、けっこう複雑な `Polygon` オブジェクトを自動で作ってくれていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/47dd7820-246b-8bfb-7f1b-a63a50923d36.png)

できた円は `Polygon` オブジェクトなので、以下のように面積を求めることもできます。

```python: 面積を求める
s[0].buffer(0.5).area # 0.7841371226364849
```

半径＝0.5の円の面積になっていますね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/a966be16-5218-aeb9-01fb-d2d3fd222de1.png)

### 線(`LineString`オブジェクト) に Buffer を与える

```python: 線（LineString オブジェクト）に Buffer を与える
s[1].buffer(0.5)
```

こうすると以下のように、`s[1]` のルートの周辺 `0.5` の範囲をカバーする図形が描画されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/da0778b0-f566-a5e5-b830-597e1cdab5d6.png)

Buffer を与えた後の `LineString` オブジェクトの情報を表示すると…

```python: Buffer を与えた後の Point オブジェクトの情報を表示
print(s[0].buffer(0.5))
```
やはりちょっと複雑めの`Polygon` オブジェクトが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/8112123d-be6f-d654-1473-ecd3eeece7c3.png)


### 図形(`Polygon` オブジェクト) に Buffer を与える

```python: 図形（Polygon オブジェクト）に Buffer を与える
s[2].buffer(0.5)
```

こうすると以下のように、`s[2]` の周辺 `0.5` の範囲をカバーする図形が描画されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/bd389b87-c1e9-189a-48f5-483cf773f3f9.png)

以下のように、もとの図形と重ねて表示すると、違いがよくわかりますね。

```python: Bufferを与える前後の図形を重ねて表示
from shapely.geometry import MultiPolygon

polygon_a = s[2]               # もとの図形
polygon_b = s[2].buffer(0.5)   # バッファあり
polygons = MultiPolygon([polygon_a, polygon_b])
polygons
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/8765c537-e720-4233-6b40-6439dcf1e4d7.png)

### GeoSeries 全体に Buffer を与える

以下のように、GeoSeries全体にまとめてBufferを与えることもできます。

```python: GeoSeries 全体にまとめて Bufferを与える
s.buffer(0.5)
```

もともとは点(`Point`オブジェクト)、線(`LineString`オブジェクト)、図形(`Polygon`オブジェクト)だった `GeoSeries` の中身が、すべて `Polygon` になっていることがわかります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587845/4de9d034-f3d5-1377-7d80-ac3ed2706b78.png)

その上で、バッファをかけた `GeoSeries` 全体からインデックスを指定して要素を取り出すこともできます。

```python: GeoSeries x Buffer x 要素の取り出し
# 今までのやりかた
s[2].buffer(0.5)

# こっちでもできる
s.buffer(0.5)[2]
```

どちらがいいのかは・・・これから勉強してわかったら書きます。

今日の記事は以上です。