はじめに
QGISでPLATEAUのCityGMLを読み込むには様々な方法があります。
たとえば、CityGMLのgmlファイルをQGISにドラッグ&ドロップしてもレイヤに追加することができますが、LOD2があるファイルはLOD2の部分を読み込むことができません。
もう一つの方法が「PLATEAU QGIS Plugin」で読み込む方法です。
しかし、PLATEAU QGIS Pluginは、1度の実行でファイル一つしか読み込むことができません。
そこで、PLATEAU QGIS Pluginを使って、同じフォルダ内のCityGMLファイル(gmlファイル)を一括で読み込んで、QGISのレイヤに追加するPythonスクリプトを作成しました。
PLATEAU GML Pluginのインストール
あらかじめQGISに「PLATEAU GML Plugin」をインストールしておいてください。
プラグインのインストールは次のページを参照しましょう。
プラグインでQGISをもっと便利に〜概要とインストール方法〜(QGIS LAB)
一括読み込みのPythonスクリプト
フォルダ内のCityGMLファイルを一括で読み込んでQGISのレイヤに追加するPythonスクリプトは次のとおりです。
"""
Model exported as python.
Name : 都市モデル読込(フォルダ内一括)
Group : PLATEAU処理
With QGIS : 34400
"""
#このスクリプトはPLATEAU QGIS Pluginを使って、フォルダ内のすべてのgmlファイルを一括でレイヤに追加します。
#プロセシングツールボックスのスクリプト→「ツールボックスにスクリプトを追加」でツールボックスに追加できます。
import os
from typing import Any, Optional
from qgis.core import (
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingContext,
QgsProcessingFeedback,
QgsProcessingMultiStepFeedback,
QgsProcessingParameterFile,
QgsProcessingParameterCrs,
QgsCoordinateReferenceSystem,
QgsProcessingException
)
from qgis import processing
class LoadCityGMLFolderAlgorithm(QgsProcessingAlgorithm):
def initAlgorithm(self, config: Optional[dict[str, Any]] = None):
# フォルダの選択
self.addParameter(
QgsProcessingParameterFile(
'citygml_folder',
'cityGMLフォルダ',
behavior=QgsProcessingParameterFile.Folder,
defaultValue=None
)
)
# CRSの選択(デフォルトは EPSG:6668)
self.addParameter(
QgsProcessingParameterCrs(
'crs',
'座標参照系(CRS)',
defaultValue='EPSG:6697'
)
)
def processAlgorithm(self, parameters: dict[str, Any], context: QgsProcessingContext, model_feedback: QgsProcessingFeedback) -> dict[str, Any]:
folder_path = parameters['citygml_folder']
crs = parameters['crs'] # 選択されたCRS
# GMLファイル一覧を取得
gml_files = [f for f in os.listdir(folder_path) if f.lower().endswith('.gml')]
total = len(gml_files)
if total == 0:
raise QgsProcessingException('指定フォルダにGMLファイルが見つかりません。')
feedback = QgsProcessingMultiStepFeedback(total, model_feedback)
results = {}
outputs = {}
for index, gml_file in enumerate(sorted(gml_files)):
feedback.setCurrentStep(index)
if feedback.isCanceled():
break
input_path = os.path.join(folder_path, gml_file)
feedback.pushInfo(f"{index+1}/{total} ファイルを処理中: {gml_file}")
alg_params = {
'APPEND_MODE': True,
'CRS': crs,
'FORCE_2D': False,
'INPUT': input_path,
'LOD_PREFERENCE': 2, #すべてのLODを読み込む
'SEMANTIC_PARTS': True
}
result = processing.run(
'plateau_plugin:load_as_vector',
alg_params,
context=context,
feedback=feedback,
is_child_algorithm=True
)
outputs[gml_file] = result
return results
def name(self) -> str:
return 'loadcitygmlfolder'
def displayName(self) -> str:
return '都市モデル読込(フォルダ内一括)'
def group(self) -> str:
return 'PLATEAU処理'
def groupId(self) -> str:
return 'plateau'
def createInstance(self):
return LoadCityGMLFolderAlgorithm()
スクリプトをプロセシングツールボックスに追加
上記のコードをテキストエディタなどにコピーして、「PLATEAU_GML2QGIS.py」というファイル名で保存します。
QGISを起動して、
-
スクリプトボタンをクリックして、「ツールボックスにスクリプトを追加」を選択し、「PLATEAU_GML2QGIS.py」を選択します。
-
プロセシングツールボックスに、「スクリプト」→「PLATEAU処理」→「都市モデル読込(フォルダ内一括)」というメニューが追加されます。
CityGMLファイルの一括処理
「都市モデル読込(フォルダ内一括)」を実行し、CityGMLが保存されているフォルダを指定して、座標参照系も選択し、「実行」ボタンをクリックすると、gmlファイルを一括読込して、レイヤに追加します。
同名のレイヤをマージするスクリプト
上記のコードでQGISにレイヤを追加すると、同じ種類のレイヤは同じ名前でレイヤに追加されます。例えば建物LOD1は「Building (LoD1)」という名前で複数のレイヤが追加されます。
そこで、次のスクリプトを実行すると、同じ名前のレイヤを一つのレイヤにマージできます。元のレイヤは削除されます。
スクリプトは、上記のスクリプトと同じように、プロセシングツールボックスのスクリプトボタンをクリックして、「ツールボックスにスクリプトを追加」を選択し、Pythonファイルを選択します。
"""
Model exported as python.
Name : 同名レイヤのマージと削除
Group : PLATEAU処理
With QGIS : 34400
"""
#このスクリプトは、プロセシングツールボックスのスクリプト→「ツールボックスにスクリプトを追加」でツールボックスに追加できます。
#「都市モデル読込(フォルダ内一括)」を実行後に、同じ名前のレイヤをマージします。
#例えば建物データを読み込むと同じ種類のレイヤは、「Building (LoD1)」などの同じ名前のレイヤ名になります。
#メニューを実行して、同じ種類のレイヤを一つのレイヤにマージします。
from qgis.core import (
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingContext,
QgsProcessingFeedback,
QgsProcessingException,
QgsProject,
QgsVectorLayer
)
import processing
class MergeDuplicateLayersAlgorithm(QgsProcessingAlgorithm):
def initAlgorithm(self, config=None):
# パラメータなし(すべての同名レイヤを対象にする)
pass
def processAlgorithm(self, parameters, context: QgsProcessingContext, feedback: QgsProcessingFeedback):
project = QgsProject.instance()
layers = list(project.mapLayers().values())
# レイヤ名でグループ化
layer_dict = {}
for layer in layers:
if not isinstance(layer, QgsVectorLayer):
continue
name = layer.name()
if name not in layer_dict:
layer_dict[name] = []
layer_dict[name].append(layer)
merged_count = 0
for name, same_name_layers in layer_dict.items():
if len(same_name_layers) < 2:
continue # 重複なし
feedback.pushInfo(f"マージ対象: {name} ({len(same_name_layers)}個)")
try:
result = processing.run("native:mergevectorlayers", {
'LAYERS': same_name_layers,
'CRS': same_name_layers[0].crs().authid(),
'OUTPUT': 'memory:' + name + '_merged'
}, context=context, feedback=feedback)
merged_layer = result['OUTPUT']
merged_layer.setName(name)
project.addMapLayer(merged_layer)
# 元のレイヤを削除
for layer in same_name_layers:
project.removeMapLayer(layer)
merged_count += 1
except Exception as e:
feedback.reportError(f"{name} のマージに失敗しました: {e}")
feedback.pushInfo(f"{merged_count} 件の重複レイヤをマージして追加しました。")
return {}
def name(self):
return 'mergeduplicatelayers'
def displayName(self):
return '同名レイヤのマージと削除'
def group(self) -> str:
return 'PLATEAU処理'
def groupId(self) -> str:
return 'plateau'
def createInstance(self):
return MergeDuplicateLayersAlgorithm()