指定した地域のメッシュコードを取得する

# はじめに
はじめまして！データサイエンティストの山上です。株式会社GEOTRA(@GEOTRA )にてGIS（Geographic Information System：地理情報システム）データを用いた分析を行っています。
本記事では指定された地域のメッシュコードを取得し、それをGeoDataFrameとして視覚化する方法を紹介します。

# やること（背景）
- 指定したエリア（ポリゴン）に含まれるメッシュコードを取得する
    - 指定された地域のメッシュを生成
    - メッシュをGeoDataFrameとして可視化
- 前提：指定エリアのポリゴン情報をデータフレームで所持している
- バージョン情報：
    - Python version 3.9.5
    - jismesh.utils version: 2.1.0
    - Shapely version: 1.8.0
    - Pandas version: 1.4.2
    - GeoPandas version: 0.10.2
    - Matplotlib version: 3.5.2
    - NumPy version: 1.22.3

# コードの概要
```python:python
# ライブラリのインポート
import jismesh.utils as ju
from shapely import wkt
from shapely.ops import unary_union
import pandas as pd
import geopandas as gpd
from shapely.geometry import Polygon
import matplotlib.pyplot as plt
import numpy as np

def make_mesh_list_semiauto(gdf: gpd.GeoDataFrame, tgt_level: int):
    # 例外処理
    if not isinstance(tgt_level, int) or not (1 <= tgt_level <= 6):
        raise ValueError("the level is unsupported.")
    
    # 最小値と最大値を設定し、最小値からstepずつ増加させて最大値を超えないリストを作成する
    def generate_list(min_value, max_value, step):
        # リストの生成
        values = list(np.arange(min_value, max_value, step))
        
        # 最大値がリストに含まれていない場合は追加
        if values[-1] < max_value:
            values.append(max_value)
            
        return values
    
    minx, miny, maxx, maxy = gdf.total_bounds # 対象のPOLYGONの最大最小緯度経度を取得
    tgt_polygon = unary_union(gdf.geometry.to_list()) # 対象のPOLYGONを結合
    
    level = 1
    # 候補となる1次メッシュコードを取得
    lon_list = generate_list(minx, maxx, 1)
    lat_list = generate_list(miny, maxy, 0.6667)
    candidate_meshcodes = list(set([ju.to_meshcode(lat, lon, level) for lat in lat_list for lon in lon_list]))
    
    # 対象のメッシュレベルまで交差判定を繰り返す
    if(tgt_level in [2,3,4,5,6]):
        while True:
            level += 1
            next_candidate_meshcodes = [meshcode for candidate_meshcode in candidate_meshcodes for meshcode in ju.to_intersects(candidate_meshcode, level)]
            lat_sw, lon_sw = ju.to_meshpoint(next_candidate_meshcodes, 0, 0)
            lat_ne, lon_ne = ju.to_meshpoint(next_candidate_meshcodes, 1, 1)
            polygon_list = [Polygon([(lon_sw[i], lat_sw[i]), (lon_sw[i], lat_ne[i]), (lon_ne[i], lat_ne[i]), (lon_ne[i], lat_sw[i])]) for i in range(len(next_candidate_meshcodes))]
            candidate_meshcodes = [next_candidate_meshcodes[i] for i in range(len(next_candidate_meshcodes)) if polygon_list[i].intersects(tgt_polygon)]
            
            if level == tgt_level:
                dict_meshcode2polygon = dict(zip(next_candidate_meshcodes, polygon_list))
                geometry = [dict_meshcode2polygon[meshcode] for meshcode in candidate_meshcodes]
                break
    else:
        if len(candidate_meshcodes) >= 2:
            lat_sw, lon_sw = ju.to_meshpoint(candidate_meshcodes, 0, 0)
            lat_ne, lon_ne = ju.to_meshpoint(candidate_meshcodes, 1, 1)
            polygon_list = [Polygon([(lon_sw[i], lat_sw[i]), (lon_sw[i], lat_ne[i]), (lon_ne[i], lat_ne[i]), (lon_ne[i], lat_sw[i])]) for i in range(len(candidate_meshcodes))]
        else:
            lat_sw, lon_sw = ju.to_meshpoint(candidate_meshcodes[0], 0, 0)
            lat_ne, lon_ne = ju.to_meshpoint(candidate_meshcodes[0], 1, 1)
            polygon_list = [Polygon([(lon_sw, lat_sw), (lon_sw, lat_ne), (lon_ne, lat_ne), (lon_ne, lat_sw)])]    
        
        lv1_candidate_meshcodes = candidate_meshcodes
        candidate_meshcodes = [lv1_candidate_meshcodes[i] for i in range(len(lv1_candidate_meshcodes)) if polygon_list[i].intersects(tgt_polygon)]
        dict_meshcode2polygon = dict(zip(lv1_candidate_meshcodes, polygon_list))
        geometry = [dict_meshcode2polygon[meshcode] for meshcode in candidate_meshcodes]
    
    result_gdf = gpd.GeoDataFrame(
        data = {
            'mesh_code': candidate_meshcodes,
        },
        geometry=geometry
    )
    
    # 結果の可視化
    fig,ax = plt.subplots()
    result_gdf.geometry.plot(ax=ax,alpha=.5)
    gdf.geometry.plot(ax=ax)
    plt.show()
    
    return result_gdf
```

# 参考：メッシュレベル

- メッシュレベル
    - 1次(標準地域メッシュ 80km四方): 1
    - 2次(標準地域メッシュ 10km四方): 2
    - 3次(標準地域メッシュ 1km四方): 3
    - 4次(分割地域メッシュ 500m四方): 4
    - 5次(分割地域メッシュ 250m四方): 5
    - 6次(分割地域メッシュ 125m四方): 6

# 事例説明

群馬県を例にとって動かしてみる

### 指定エリア（ポリゴン）が小さい場合

↓  125mメッシュの場合

![677E4B79-B177-46D4-AA48-E42A47EDEEE9.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3824265/8b3f9b29-6fc6-a74c-d50c-7fb0c171c794.jpeg)

6次メッシュ（標準地域メッシュ 125m四方）

```python:python
areas = #ポリゴンのデータフレーム
areas.geometry = areas.geometry.apply(wkt.loads)
areas = gpd.GeoDataFrame(areas,geometry='geometry',crs='EPSG:4326')
gunnma = make_mesh_list_semiauto(areas, 6) # << 6次メッシュなので6に設定
```

# 指定エリア（ポリゴン）が巨大な場合
↓  1kmメッシュの場合

![2F09CCF6-8B16-487D-8E4E-9973B6CF8E57.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3824265/f199ee77-c8b6-6c14-9705-97505b8bd335.jpeg)

３次メッシュ（標準地域メッシュ 1km四方）

```python:python
areas = #ポリゴンのデータフレーム
areas.geometry = areas.geometry.apply(wkt.loads)
areas = gpd.GeoDataFrame(areas,geometry='geometry',crs='EPSG:4326')
gunnma = make_mesh_list_semiauto(areas, 3) # << 3次メッシュなので3に設定
```

## 参考文献

[メッシュデータについて | 国土数値情報ダウンロードサイト](https://nlftp.mlit.go.jp/ksj/old/old_data_mesh.html)

[令和二年度　国勢調査　メッシュ定義書 | 政府統計の総合窓口](https://www.e-stat.go.jp/help/data-definition-information/downloaddata/B002005212020.pdf)

[The Shapely User Manual - Shapely 2.0.4 documentation](https://shapely.readthedocs.io/en/stable/manual.html#set-theoretic-methods)

[Google Earthを用いたポリゴン作成 | Qiita](https://qiita.com/nabe189/items/6bf94f93fa3f44753337)

[日本の地域メッシュを地図上に可視化して理解する | Qiita](https://qiita.com/kota9/items/3cd8eb8f3df6d00e232b)

# 終わりに
最後までお読みくださりありがとうございました。

GEOTRAのQiitaでは、引き続き地理空間情報に関する情報を中心に技術系のテクニックを発信していきます。今後も皆さんのお役に立てるコンテンツを配信できればと思っておりますので、皆様のいいね・フォローをお待ちしています！

また、こちらの記事を読んでGEOTRAに興味を持ってくださった方は、是非[Wantedly](https://www.wantedly.com/stories/s/geotra_introduction)から会社紹介記事もご覧ください！GEOTRAは、学生インターンを含め、現在一緒に働く仲間を募集しております。興味をお持ちの方は、Wantedly上の募集ページやストーリー、各種サイトをご確認ください！

- **企業HP：**[https://www.geotra.jp](https://www.geotra.jp/)
- **note：**[https://note.com/2022geotra](https://note.com/2022geotra/)
- **Wantedly：**[https://www.wantedly.com/stories/geotra_introduction](https://www.wantedly.com/stories/s/geotra_introduction)
- **LinkedIn：**[https://www.linkedin.com/company/geotra/](//www.linkedin.com/company/%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BEgeotra/)

#Pythonプログラミング, #GeoDataFrame, #地理情報システム(GIS), #メッシュコード
#地図データ, #データ可視化, #ジオメトリ, #Shapely, #GeoPandas, #マッププロット