PyQGISでレイヤ一覧を取得する方法のまとめ

# 概要
QGISのプラグイン開発やPythonコンソールにおいてレイヤの一覧を取得する方法はいくつかありますが、それぞれ出力されるオブジェクトの種類や順序、内容に違いがあるので、代表的なものを整理したいと思います。

# 実行環境
- QGIS 3.28.7 LTR
    - Python 3.9.5


# 対象とするレイヤツリーの状態
以下のようなレイヤツリーの状態に対して取得される内容を確認していきます。

![レイヤツリーの状態](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/591150/a44db820-c7e6-1c81-9445-a0bfb38a54ae.png)

以下では、QGISのPythonコンソールで出力を確認していますが、もちろんプラグイン等でも利用可能です。

# QgsProject.mapLayers()
ドキュメント：

https://qgis.org/pyqgis/3.30/core/QgsProject.html#qgis.core.QgsProject.mapLayers

戻り値の型： `Any`

プロジェクトのオブジェクトから呼び出せるメソッドで、一番基本的な方法と思われます。呼び出しの例と取得内容は以下の通りです。

```:Pythonコンソール
>>> QgsProject.instance().mapLayers()
{
    'OpenStreetMap_7c89aabd_a990_4e34_8b1c_3dfdf75fd65b': <QgsRasterLayer: 'OpenStreetMap' (wms)>,
    'boundary_b0675ee4_67da_427b_bdd6_4a1debbbc10f': <QgsVectorLayer: '行政区域' (ogr)>,
    'photograph_61c3b74b_0eea_419d_9788_d19dab183b58': <QgsRasterLayer: '航空写真' (gdal)>,
    'police_office_e88894f5_cce1_4060_bbbb_a59945429a03': <QgsVectorLayer: '警察署' (ogr)>,
    'public_office_afbbe9d4_9fd2_4251_b0b5_1b51379ee3f2': <QgsVectorLayer: '役所' (ogr)>,
    'railroad_37beb2fd_9798_45ea_85fe_f3419d55cac3': <QgsVectorLayer: '鉄道' (ogr)>,
    'road_b9d2bccb_a2aa_44f7_9c95_a04ac766bf97': <QgsVectorLayer: '道路' (ogr)>,
    'shopping_171a8efa_2eb1_4f7c_a96a_eb17a2b67162': <QgsVectorLayer: '商業施設' (ogr)>
}
```
※出力は見やすくなるように記事上で空白と改行を追加しています。これ以降の出力も同様です。

このメソッドの型ヒントとしては `Any` が設定されていますが、基本的にはレイヤ固有の文字列をキーとした辞書型 `Dict[str, QgsMapLayer]` として返されるようです。この辞書の順番はレイヤツリーでの表示順と整合していないため、表示順で取得する必要がある際は他の方法を使う必要があります。

# QgsLayerTree.layerOrder()
ドキュメント：

https://qgis.org/pyqgis/3.30/core/QgsLayerTree.html#qgis.core.QgsLayerTree.layerOrder

戻り値の型： `List[QgsMapLayer]`

これはレイヤツリーの表示順に従ってリストで取得する方法です。呼び出しの例と取得内容は以下の通りです。

```:Pythonコンソール
>>> QgsProject().instance().layerTreeRoot().layerOrder()
[
    <QgsVectorLayer: '商業施設' (ogr)>,
    <QgsVectorLayer: '役所' (ogr)>,
    <QgsVectorLayer: '警察署' (ogr)>,
    <QgsVectorLayer: '道路' (ogr)>,
    <QgsVectorLayer: '鉄道' (ogr)>,
    <QgsVectorLayer: '行政区域' (ogr)>,
    <QgsRasterLayer: '航空写真' (gdal)>,
    <QgsRasterLayer: 'OpenStreetMap' (wms)>
]
```

レイヤツリーの表示順と同じ順番のリストが取得できていることが確認できます。

# QgsLayerTreeNode.checkedLayers()
ドキュメント：

https://qgis.org/pyqgis/3.30/core/QgsLayerTreeNode.html#qgis.core.QgsLayerTreeNode.checkedLayers

戻り値の型： `List[QgsMapLayer]`

これはチェックを入れているレイヤを対象として、レイヤツリーの表示順で取得する方法です。呼び出しの例と取得内容は以下の通りです。

```:Pythonコンソール
>>> QgsProject.instance().layerTreeRoot().checkedLayers()
[
    <QgsVectorLayer: '商業施設' (ogr)>,
    <QgsVectorLayer: '役所' (ogr)>,
    <QgsVectorLayer: '道路' (ogr)>,
    <QgsVectorLayer: '行政区域' (ogr)>,
    <QgsRasterLayer: '航空写真' (gdal)>,
    <QgsRasterLayer: 'OpenStreetMap' (wms)>
]
```

チェックを入れているレイヤのみ取得できていることが確認できます。

# QgsLayerTreeNode.children()
ドキュメント：

https://qgis.org/pyqgis/3.30/core/QgsLayerTreeNode.html#qgis.core.QgsLayerTreeNode.children

戻り値の型： `List[QgsLayerTreeNode]`

この方法はレイヤーのオブジェクトを取得するのではなく、レイヤツリーの項目としてのオブジェクトを取得します。呼び出しの例と取得内容は以下の通りです。

```:Pythonコンソール
>>> QgsProject.instance().layerTreeRoot().children()
[
    <QgsLayerTreeGroup: 施設>,
    <QgsLayerTreeLayer: 道路>,
    <QgsLayerTreeLayer: 鉄道>,
    <QgsLayerTreeLayer: 行政区域>,
    <QgsLayerTreeLayer: 航空写真>,
    <QgsLayerTreeLayer: OpenStreetMap>
]
```

レイヤツリーの階層構造に従って、最上位にあるレイヤやグループの項目が取得できています。グループ配下の項目を取得するためには、以下のように再帰的に `children()` メソッドを呼び出すことで取得可能です。

```:Pythonコンソール
>>> QgsProject.instance().layerTreeRoot().children()[0].children()
[
    <QgsLayerTreeLayer: 商業施設>,
    <QgsLayerTreeGroup: 行政施設>
]
```

また、ここで取得されるレイヤは `QgsVectorLayer` や `QgsRasterLayer` 等の `QgsMapLayer` オブジェクトではなく、レイヤツリー上の項目としてのオブジェクトである `QgsLayerTreeLayer` が取得されます。
この `QgsLayerTreeLayer` オブジェクトから `QgsMapLayer` を取得するためには、以下のように `layer()` メソッドを呼び出します。

```:Pythonコンソール
>>> QgsProject.instance().layerTreeRoot().children()[1].layer()
<QgsVectorLayer: '道路' (ogr)>
```

`QgsLayerTreeLayer` オブジェクトから `QgsVectorLayer` が取得されています。

# QgsLayerTreeGroup.findLayers()
ドキュメント：

https://qgis.org/pyqgis/3.30/core/QgsLayerTreeGroup.html#qgis.core.QgsLayerTreeGroup.findLayers

戻り値の型： `List[QgsLayerTreeLayer]`

これは配下の全ての `QgsLayerTreeLayer` オブジェクト一覧を取得する方法です。呼び出しの例と取得内容は以下の通りです。

```:Pythonコンソール
>>> QgsProject.instance().layerTreeRoot().findLayers()
[
    <QgsLayerTreeLayer: 商業施設>,
    <QgsLayerTreeLayer: 役所>,
    <QgsLayerTreeLayer: 警察署>,
    <QgsLayerTreeLayer: 道路>,
    <QgsLayerTreeLayer: 鉄道>,
    <QgsLayerTreeLayer: 行政区域>,
    <QgsLayerTreeLayer: 航空写真>,
    <QgsLayerTreeLayer: OpenStreetMap>
]
```

グループは取得されず、表示順で全ての `QgsLayerTreeLayer` オブジェクトが取得されていることが確認できます。

# 参考文献

https://gis.stackexchange.com/questions/339394/listing-of-vector-layers

https://gis.stackexchange.com/questions/150611/getting-list-of-layers-names-using-pyqgis

https://gis.stackexchange.com/questions/312040/listing-loaded-layers-in-qgis-with-pyqgis

https://qiita.com/kntoshiya/items/ed830d5c64ece56b2ee5