一括ファイルチェック(GeoPandas)

  • 指定フォルダ内のShapefile(.shp)を一括処理します。
  • Shapefileから以下の情報を抽出します。
    • ファイル名
    • ファイル名から推測される河川名
    • メッシュのX方向とY方向のサイズ(最初のポリゴンのバウンディングボックスから算出)
    • 座標系(CRS)
    • ポリゴンの数
    • 属性(列名)の一覧
  • 各Shapefileの図形が四角形のポリゴンであるかを検証します。
    • 四角形でないポリゴンやポリゴン以外の図形が含まれる場合、そのファイルはエラーとして報告されます。
  • 複数ファイルを並列で処理し、処理時間を短縮します。
  • 抽出した情報を整形し、指定されたテキストファイルに書き出します。
# 開発環境 python 3.9.9 geopandas
# メッシュデータ情報を一括取得 並列処理

import os
import glob
import geopandas as gpd
import pandas as pd
from multiprocessing import Pool
import time

CPU_THREAD = 4

def func():
    # read
    print("start...")
    start = time.time()

    # パスの入力
    folderPath = r""
    path_txt = r""
    files = sorted(glob.glob(folderPath+'\\*.shp'), reverse=False)
    str_info = ""

    # 並列
    with Pool(CPU_THREAD) as pool:
        sleeps = [pool.apply_async(info, (i, )) for i in files]
        d = [f.get() for f in sleeps]
        str_info = '\n'.join(d)

    # write
    write(path_txt, str_info)

    print(time.time() - start)
    print("finish")


def info(path_shp):
    # ファイル名の処理
    filename = os.path.splitext(os.path.basename(path_shp))[0]
    filename_s = filename.split("_")  # Result_111_○○○.shp

    print(filename_s)

    # データの取得
    data = gpd.read_file(path_shp, encoding="shift_jis", row=5)

    count_f = len(data)  # ポリゴンの数の取得

    # 四角形じゃない判定(全体)
    df = pd.DataFrame(data)
    for i, row in df.iterrows():
        if (len(list(row["geometry"].exterior.coords)) != 5):
            # print("not 四角形 全体で検索")
            return filename+":"+'not 四角形'
    # 実行
    df = pd.DataFrame(data.head(3))
    zahyou = df.iat[0, -1]
    str_data = ""

    if (zahyou.geom_type == "Polygon"):
        # type polygon
        if (len(list(zahyou.exterior.coords)) == 5):
            # 5points
            str_data = filename + ','+filename_s[1] + ',' + \
                str(round(zahyou.bounds[2]-zahyou.bounds[0], 15)) + ',' + \
                str(round(zahyou.bounds[3]-zahyou.bounds[1], 15)) + ',' + \
                str(data.crs) + ',' + \
                str("ポリゴン数⇒") + ',' + str(count_f) + ',' + \
                str("属性⇒") + ',' + \
                str(str(df.columns.tolist())
                    .replace('[', '').replace(']', '').replace("'", ''))

            return str_data
        else:
            return filename+":"+'not 四角形'

    else:
        return filename+":"+'not polygon'


def write(path_txt, str_data):
    with open(path_txt, 'w') as f:
        f.writelines(str_data)


if __name__ == "__main__":
    func()

一括図面出力スクリプト(ArcGIS Pro)

このArcGIS Proスクリプトは、ArcGIS Proプロジェクト内の特定のレイアウトから複数の地図画像を自動生成するためのツールです。具体的には、以下の処理を行います。

  • ArcGIS Proの入力パラメータを取得します。
    • 対象とするレイヤーグループ名、出力形式(PNG, PDF, JPG)、解像度、出力フォルダ、レイアウト名、マップフレーム名、マップ名、図郭レイヤーとそのフィールド名、設定用ExcelファイルのパスをArcGIS Proのスクリプトツールとして受け取ります。
  • Excel設定ファイルから情報を読み込みます。
    • Excelファイルは、レイヤー名とそれに対応する図郭フィールドの値のリスト、そして出力ファイル名やレイアウト上のテキスト要素に表示するテキストの情報を含んでいます。
  • 指定されたグループ内のレイヤーを繰り返し処理します。
    • Excelファイルで指定された各レイヤーについて、そのレイヤーを可視化します。
    • Excelで指定された図郭フィールドの値に基づいて、図郭レイヤーに定義クエリを設定し、マップフレームの表示範囲をその図郭に合わせます。
    • マップフレームの縮尺を調整します(例: 2500, 5000などの特定の縮尺に丸めます)。
    • レイアウト上の特定のテキスト要素(txt_ele001を含む名前の要素)に、Excelから読み込んだテキストを設定します。
  • 各マップを画像として出力します。
    • PNG、PDF、またはJPG形式で、指定された解像度で画像をエクスポートします。出力ファイル名はExcelファイルから取得します。
    • 画像出力後、対応するレイヤーの表示を非表示に戻します。
  • 処理の最後に図郭レイヤーの定義クエリをリセットし、メッセージを出力します。
# ArcGIS Pro
# 画像出力スクリプト

import arcpy
import pandas as pd
import openpyxl

"""
グループに入っているレイヤーから画像出力
(0):グループ名
(1):出力フォーマット
(2):解像度
(3):出力フォルダ
(4):レイアウト名
(5):マップフレーム
(6):マップ名
(7):図郭 レイヤー
(8):図郭 フィールド名
(9):excelファイルパス 設定ファイル

excelファイル構成
1列目:レイヤー名
2列目:(7):図郭 フィールド名の値一覧

"""

def func():
    # 読み込み
    gLayerName = arcpy.GetParameterAsText(0)
    format = arcpy.GetParameterAsText(1)
    reso = arcpy.GetParameterAsText(2)
    exportFolder = arcpy.GetParameterAsText(3) + "\\\\"
    layoutName = arcpy.GetParameterAsText(4)
    mapFrameName = arcpy.GetParameterAsText(5)
    mapName = arcpy.GetParameterAsText(6)
    lyr_zukaku = arcpy.GetParameterAsText(7)  # zukaku レイヤー
    lyr_zukaku_fn = arcpy.GetParameterAsText(8)  # zukaku フィールド名
    excel_file = arcpy.GetParameterAsText(9)  # excel

    #####################
    # マップ構成の読み込み
    project = arcpy.mp.ArcGISProject("CURRENT")
    layout = project.listLayouts(str(layoutName))[0]
    map = project.listMaps(str(mapName))[0]
    lyrs = map.listLayers()
    lyr_zukakua = map.listLayers(str(lyr_zukaku))[0]
    lyr_zukaku_fn_Type = arcpy.ListFields(lyr_zukakua, lyr_zukaku_fn)[0].type

    # グループレイヤー「export」のみレイヤーの取得
    exportLyr = []
    for lyr in lyrs:
        if lyr.isGroupLayer is False:
            if lyr.longName.split("\\")[0] == gLayerName:
                exportLyr.append(lyr)
                lyr.visible = False

    # マップフレームの取得
    mf=layout.listElements("mapframe_element", mapFrameName)[0]
    txt_elements = layout.listElements('TEXT_ELEMENT', "*txt_ele001*")

    # excelファイルの読み込み
    df = reader_excel(excel_file)

    #####################
    if format == "png":
        if exportLyr is not None:
            for lyr in exportLyr:
                lyr.visible = True
                str_fileter, filename, str_txt_ele = get_value(df, lyr.name, lyr_zukaku_fn_Type)
                if len(txt_elements) > 0:
                    txt_elements[0].text = str_txt_ele
                set_pan(
                    mf,
                    lyr_zukakua,
                    '"' + lyr_zukaku_fn + '"' + "=" +
                    str_fileter,
                )
                layout.exportToPNG(
                    exportFolder + filename + ".png", resolution=int(reso)
                )
                lyr.visible = False
                arcpy.AddMessage(lyr.name + ":" + str(mf.camera.scale))
    if format == "PDF":
        if exportLyr is not None:
            for lyr in exportLyr:
                lyr.visible = True
                str_fileter, filename, str_txt_ele = get_value(df, lyr.name, lyr_zukaku_fn_Type)
                if len(txt_elements) > 0:
                    txt_elements[0].text = str_txt_ele
                set_pan(
                    mf,
                    lyr_zukakua,
                    '"' + lyr_zukaku_fn + '"' + "=" +
                    get_str_filter(lyr_zukaku_fn_Type, df, lyr.name, 1),
                )
                layout.exportToPDF(
                    exportFolder + filename + ".pdf",
                    resolution=int(reso),
                    layers_attributes="NONE",
                )
                lyr.visible = False
                arcpy.AddMessage(lyr.name + ":" + str(mf.camera.scale))
    if format == "jpg":
        if exportLyr is not None:
            for lyr in exportLyr:
                lyr.visible = True
                str_fileter, filename, str_txt_ele = get_value(df, lyr.name, lyr_zukaku_fn_Type)
                if len(txt_elements) > 0:
                    txt_elements[0].text = str_txt_ele
                set_pan(
                    mf,
                    lyr_zukakua,
                    '"' + lyr_zukaku_fn + '"' + "=" +
                    get_str_filter(lyr_zukaku_fn_Type, df, lyr.name, 1),
                )
                layout.exportToJPEG(
                    exportFolder + filename + ".jpg", resolution=int(reso)
                )
                lyr.visible = False
                arcpy.AddMessage(lyr.name + ":" + str(mf.camera.scale))

    lyr_zukakua.definitionQuery = ""
    arcpy.AddMessage("AllFinish")


# フィルター設定
def set_pan(mf, lyr, str1):
    if str1[-4:] == "9999":
        arcpy.AddError("error fileter setting:" + lyr.name)
        exit()
    else:
        lyr.definitionQuery = str1
        ex = ex_mergin(mf.getLayerExtent(lyr, False, True), 50)
        mf.camera.setExtent(ex)
        mf.camera.scale = get_scale(mf.camera.scale)


# int:scale
def get_scale(scale):
    if scale < 2500:
        return 2500
    elif scale < 5000:
        return 5000
    elif scale < 7500:
        return 7500
    elif scale < 10000:
        return 10000
    elif scale < 15000:
        return 15000
    elif scale < 20000:
        return 20000
    elif scale < 25000:
        return 25000
    elif scale < 30000:
        return 30000
    elif scale < 40000:
        return 40000
    elif scale < 50000:
        return 50000


def ex_mergin(ex, mergin):
    ex.XMin = ex.XMin - mergin
    ex.YMin = ex.YMin - mergin
    ex.XMax = ex.XMax + mergin
    ex.YMax = ex.YMax + mergin
    return ex


def get_str_filter(fType, df, indexName, coli):
    # フィルターのクエリの構文を返す
    # 対象がString型とint型で異なるので判定している
    value = str(df.at[indexName, df.columns[coli]])
    if fType == "String":
        return "'"+str(value)+"'"
    elif fType == "Integer":
        return str(value)
    else:
        return str(value)


def get_value(df, indexName, fType):
    value = str(df.at[indexName, df.columns[1]])
    if fType == "String":
        str_fileter = "'" + str(value) + "'"
    elif fType == "Integer":
        str_fileter = str(value)
    else:
        str_fileter = str(value)

    return str_fileter, str(df.at[indexName, df.columns[2]]), str(df.at[indexName, df.columns[3]])


def reader_excel(pathCSV):
    """
    line1
    """
    wb = openpyxl.load_workbook(pathCSV, data_only=True)
    sheet = wb[wb.sheetnames[0]]
    data = list(sheet.values)
    col = []
    for dddd in data:
        col.append(dddd[0])
    return pd.DataFrame(data[1:], columns=data[0], index=col[1:])


if __name__ == "__main__":
    func()

QGIS シェープファイル一括で読み込む

このPythonスクリプトは、QGISのマップ上に、指定したフォルダ構造に基づいてシェープファイル(.shp)をグループ化して一括で読み込むためのものです。QGISのPythonコンソールで実行することを想定しています。

  • 指定したベースフォルダ以下を探索します。
    • base_folder_pathに設定されたパスから、サブフォルダを再帰的に検索します。
  • フォルダ構造をQGISのグループとして再現します。
    • 各サブフォルダはQGISのグループとして扱われ、ネストされたフォルダ構造はネストされたグループとしてQGISのレイヤーパネルに表示されます。
    • 既存のグループがあればそれを使用し、なければ新しく作成します。
    • 大量のデータ読み込み時のパフォーマンスを考慮し、新しく作成または見つけたグループはデフォルトで非表示になります。
  • 各フォルダ内のシェープファイルをレイヤーとして読み込みます。
    • 各グループ(フォルダ)内で見つかったシェープファイルは、そのファイル名(拡張子なし)をレイヤー名としてQGISに読み込まれます。
    • 読み込まれたレイヤーは、対応するグループの子要素として追加されます。
    • 無効なシェープファイルがあれば、警告メッセージを出力してスキップします。

      このスクリプトは、多数のシェープファイルが階層的なフォルダ構造で整理されている場合に、それらをQGISのレイヤーパネルに効率的に整理して読み込むのに役立ちます。
"""
QGIS Pythonコンソールで実行
QGISのマップ上にシェープファイルをグループ毎に一括で読み込むスクリプト
グループ名=フォルダ名
レイヤー名=シェープファイル名(拡張子なし)

1.フォルダの階層を取得
2.グループ毎にファイルの追加
"""

import glob
import os

from qgis.core import QgsLayerTreeLayer, QgsProject, QgsVectorLayer


def func():
    # 任意のフォルダを指定
    base_folder_path = r"E:\Temp"

    for curDir, dirs, _ in os.walk(base_folder_path):
        files = glob.glob(os.path.join(curDir, "*.shp"))
        print("===================")
        print("現在のディレクトリ: " + curDir)
        print("ディレクトリ数:" + str(len(dirs)))
        print("シェープファイル数: " + str(len(files)))

        Add_Group(curDir.replace(base_folder_path, ""), files)

    print("AllFinish")


def Add_Group(group_name, filelist):
    # 階層に分割
    splite_group_name = group_name.split("\\")

    qgs_project = QgsProject.instance()
    root_node = qgs_project.layerTreeRoot()

    if len(splite_group_name) > 0:
        # グループを見つける なければ新規作成
        for gname in splite_group_name:
            if gname != "":
                existing_group = root_node.findGroup(gname)
                if existing_group:
                    group_node = existing_group
                    print(f"  既存のグループ '{gname}' を使用します。")
                else:
                    group_node = root_node.addGroup(gname)  # 新しいグループノードを作成
                    print(f"  新しいグループ '{gname}' を作成しました。")
                # 非表示にする 大量データ時 重くなるため
                group_node.setItemVisibilityChecked(False)
                # rootグループの設定
                root_node = group_node

    # グループにレイヤーの追加
    if len(filelist) > 0:
        for f in filelist:
            layer_name = os.path.splitext(os.path.basename(f))[0]
            layer_object = QgsVectorLayer(f, layer_name, "ogr")
            if not layer_object.isValid():
                print(f"  警告: レイヤー '{layer_name}' の読み込みに失敗しました: {f}")
                continue
            # QGISプロジェクトにレイヤーオブジェクトを登録 (ツリーには追加しない)
            qgs_project.addMapLayer(layer_object, False)
            # レイヤーオブジェクトからツリーノードを作成
            new_layer_node = QgsLayerTreeLayer(layer_object)
            # レイヤーノードをターゲットグループの子として追加
            root_node.insertChildNode(-1, new_layer_node)
    else:
        print("  追加するファイルはありません")


func()