Repository: MIERUNE/csmap-qgis-plugin
Pull request #46: プロセシングアルゴリズム化
Author: login: Geo-Jagaimo
Created: 2025-04-01T04:46:08Z | Merged: 2025-04-14T04:51:54Z

<!-- Close or Related Issues -->
Close #19 

### What I did（変更内容）
<!-- Please describe the motivation behind this PR and the changes it introduces. -->
<!-- どのような変更をしますか？ 目的は？ -->

- [uv.lockを.gitignoreに記述しない](https://github.com/MIERUNE/csmap-qgis-plugin/commit/22284648a0e4fd3e77c050c0da6601b25547495a)
- [metadataでプロセシング機能を宣言](https://github.com/MIERUNE/csmap-qgis-plugin/commit/158e8e77189fcb8b585972e6da666ae843b2e97d)
- [プロセシングアルゴリズム化](https://github.com/MIERUNE/csmap-qgis-plugin/commit/7099faf209d492e1b16c4e0e2bd76c03458b5789)
    - dem_to_csmap.pyの削除
    - dem_to_csmap.uiの削除
    - processing_providerフォルダ内に処理を実装


### Notes（連絡事項）
<!-- If manual testing is required, please describe the procedure. -->
<!-- 手動での動作確認が必要なら手順を簡単に伝えてください。そのほか連絡事項など。 -->

None / なし


Linked issue #19: プロジェクトに存在するラスターレイヤーの入力に対応する
QGISに追加されているラスターレイヤーを入力したいことがあるかもしれない
ただしcsmap-pyは「ファイル」のみに対応していて、これは変える必要性はないだろう

QGISに追加されているラスターレイヤーの、ファイルの実態を参照することは出来るので、これを利用すればいいだろう
Linked issue comments: user: login: Geo-Jagaimo
created_at: 2024-06-06T07:56:02Z
body: q:

QGISでラスタレイヤをエクスポートする際に選択可能な拡張子はこのプラグインでも選択可能にする，という認識で良いでしょうか？
author_association: MEMBER
user: login: Kanahiro
created_at: 2024-06-06T13:09:10Z
body: a:

拡張子までは特に考えていなかったです。ラスターレイヤー、とだけ（XYZタイルなどのレイヤーはもちろん対象外として）。
author_association: MEMBER

Diff:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index 9a669ab..3db95ab 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -46,7 +46,3 @@ jobs:
       - name: Start Xvfb in container
         run: |
           docker exec -d qgis-testing-environment sh -c "Xvfb :99 -screen 0 1024x768x24"
-
-      - name: Docker run plugin tests
-        run: |
-          docker exec qgis-testing-environment sh -c "qgis_testrunner.sh plugin_dir.test_suite.test_package"
diff --git a/.gitignore b/.gitignore
index 2c663f5..e6b6d4f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,5 +6,4 @@ __pycache__/
 .pytest_cache
 coverage.xml
 .coverage
-requirements.txt
-uv.lock
\ No newline at end of file
+requirements.txt
\ No newline at end of file
diff --git a/__init__.py b/__init__.py
index deb4cd2..60da19c 100644
--- a/__init__.py
+++ b/__init__.py
@@ -1,12 +1,16 @@
-import sys
 import os
+import sys
+
+from qgis._gui import QgisInterface
+
+from .plugin import CSMapPlugin
 
-# to import modules as non-relative
-sys.path.append(os.path.dirname(__file__))
-sys.path.append(os.path.join(os.path.dirname(__file__), "csmap_py"))
+sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "csmap_py"))
 
 
-def classFactory(iface):
-    from qcsmap import QCsMap
+def classFactory(iface: QgisInterface):
+    """
+    Entrypoint for QGIS plugin.
+    """
 
-    return QCsMap(iface)
+    return CSMapPlugin(iface)
diff --git a/csmap_py/csmap/process.py b/csmap_py/csmap/process.py
index bd8cfb0..c4580fd 100644
--- a/csmap_py/csmap/process.py
+++ b/csmap_py/csmap/process.py
@@ -1,14 +1,13 @@
+from concurrent import futures
 from dataclasses import dataclass
 from threading import Lock
-from concurrent import futures
 
 import numpy as np
 import rasterio
-from rasterio.windows import Window
 from rasterio.transform import Affine
+from rasterio.windows import Window
 
-from csmap import calc
-from csmap import color
+from . import calc, color
 
 
 @dataclass
diff --git a/dem_to_csmap.py b/dem_to_csmap.py
deleted file mode 100644
index a771bb4..0000000
--- a/dem_to_csmap.py
+++ /dev/null
@@ -1,92 +0,0 @@
-import multiprocessing
-import os
-
-from PyQt5.QtCore import Qt
-from PyQt5.QtWidgets import QDialog
-from qgis.core import Qgis, QgsProject, QgsRasterLayer
-from qgis.gui import QgsFileWidget
-from qgis.PyQt import uic
-from qgis.utils import iface
-
-from csmap_py.csmap import process
-
-
-class DemToCsMap(QDialog):
-    def __init__(self):
-        super().__init__()
-        self.ui = uic.loadUi(
-            os.path.join(os.path.dirname(__file__), "dem_to_csmap.ui"), self
-        )
-
-        # ウィンドウを常に全面に表示する
-        self.setWindowFlags(Qt.WindowStaysOnTopHint)
-
-        # 入力データの制限
-        self.ui.mQgsFileWidget_input.setFilter("*")
-
-        # 出力データの設定
-        self.ui.mQgsFileWidget_output.setFilter("*.tif")
-        self.ui.mQgsFileWidget_output.setStorageMode(QgsFileWidget.StorageMode.SaveFile)
-
-        # デフォルトの max_workers の値をCPUの最大スレッド数に設定
-        self.ui.spinBoxMaxWorkers.setValue(multiprocessing.cpu_count())
-
-        # ボタンのクリックイベント
-        self.ui.pushButton_run.clicked.connect(self.convert_dem_to_csmap)
-        self.ui.pushButton_cancel.clicked.connect(self.close)
-
-    def convert_dem_to_csmap(self):
-        # パラメータの設定
-        params = process.CsmapParams(
-            gf_size=self.ui.spinBoxGfSize.value(),
-            gf_sigma=self.ui.spinBoxGfSigma.value(),
-            curvature_size=self.ui.spinBoxCurvatureSize.value(),
-            height_scale=(
-                self.ui.spinBoxHeightScaleMin.value(),
-                self.ui.spinBoxHeightScaleMax.value(),
-            ),
-            slope_scale=(
-                self.ui.doubleSpinBoxSlopeScaleMin.value(),
-                self.ui.doubleSpinBoxSlopeScaleMax.value(),
-            ),
-            curvature_scale=(
-                self.ui.doubleSpinBoxCurvatureScaleMin.value(),
-                self.ui.doubleSpinBoxCurvatureScaleMax.value(),
-            ),  # 曲率
-        )
-
-        # 入力・出力をUIで操作
-        input_path = self.ui.mQgsFileWidget_input.filePath()
-        output_path = self.ui.mQgsFileWidget_output.filePath()
-
-        try:
-            process.process(
-                input_path,
-                output_path,
-                chunk_size=self.ui.spinBoxChunkSize.value(),
-                params=params,
-                max_workers=self.ui.spinBoxMaxWorkers.value(),
-            )
-        except Exception as e:
-            iface.messageBar().pushMessage(
-                "CSMap Plugin",
-                f"DEMデータの処理中に問題が発生しました: {e}",
-                level=Qgis.Critical,
-            )
-            return
-
-        # 出力結果をQGISに追加・マップキャンバスの中心に表示
-        rlayer = QgsRasterLayer(output_path, os.path.basename(output_path))
-        QgsProject.instance().addMapLayer(rlayer)
-        iface.setActiveLayer(rlayer)
-        iface.zoomToActiveLayer()
-
-        iface.messageBar().pushMessage(
-            "CSMap Plugin",
-            f"変換が完了しました: {output_path}",
-            level=Qgis.Info,
-        )
-
-        # 処理終了後にウィンドウを閉じるオプション
-        if self.ui.checkBox_closeAfterProcessing.isChecked():
-            self.close()
diff --git a/dem_to_csmap.ui b/dem_to_csmap.ui
deleted file mode 100644
index ce8c9ef..0000000
--- a/dem_to_csmap.ui
+++ /dev/null
@@ -1,594 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>Dialog</class>
- <widget class="QDialog" name="Dialog">
-  <property name="geometry">
-   <rect>
-    <x>0</x>
-    <y>0</y>
-    <width>478</width>
-    <height>433</height>
-   </rect>
-  </property>
-  <property name="windowTitle">
-   <string>CSMap Plugin</string>
-  </property>
-  <layout class="QVBoxLayout" name="verticalLayout">
-   <item>
-    <layout class="QGridLayout" name="gridLayout_2">
-     <item row="0" column="1">
-      <widget class="QgsFileWidget" name="mQgsFileWidget_input">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="minimumSize">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
-       </property>
-      </widget>
-     </item>
-     <item row="0" column="0">
-      <widget class="QLabel" name="label">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="minimumSize">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>入力ファイル</string>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="1">
-      <widget class="QgsFileWidget" name="mQgsFileWidget_output">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="minimumSize">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
-       </property>
-      </widget>
-     </item>
-     <item row="1" column="0">
-      <widget class="QLabel" name="label_2">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="minimumSize">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>出力ファイル</string>
-       </property>
-       <property name="alignment">
-        <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-       </property>
-      </widget>
-     </item>
-    </layout>
-   </item>
-   <item>
-    <widget class="QGroupBox" name="groupBox">
-     <property name="sizePolicy">
-      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
-       <horstretch>0</horstretch>
-       <verstretch>0</verstretch>
-      </sizepolicy>
-     </property>
-     <property name="title">
-      <string>高度なオプション</string>
-     </property>
-     <property name="checkable">
-      <bool>true</bool>
-     </property>
-     <property name="checked">
-      <bool>false</bool>
-     </property>
-     <layout class="QGridLayout" name="gridLayout">
-      <item row="5" column="1">
-       <widget class="QLabel" name="label_12">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
-          <horstretch>0</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <property name="minimumSize">
-         <size>
-          <width>60</width>
-          <height>20</height>
-         </size>
-        </property>
-        <property name="text">
-         <string>min</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="5" column="0">
-       <widget class="QLabel" name="label_9">
-        <property name="text">
-         <string>slope scale</string>
-        </property>
-       </widget>
-      </item>
-      <item row="3" column="6">
-       <widget class="QSpinBox" name="spinBoxHeightScaleMax">
-        <property name="maximumSize">
-         <size>
-          <width>80</width>
-          <height>16777215</height>
-         </size>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="maximum">
-         <number>10000</number>
-        </property>
-        <property name="singleStep">
-         <number>100</number>
-        </property>
-        <property name="value">
-         <number>1000</number>
-        </property>
-       </widget>
-      </item>
-      <item row="3" column="0">
-       <widget class="QLabel" name="label_10">
-        <property name="text">
-         <string>height scale</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="3" column="1">
-       <widget class="QLabel" name="label_7">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Minimum" vsizetype="Preferred">
-          <horstretch>0</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <property name="minimumSize">
-         <size>
-          <width>0</width>
-          <height>0</height>
-         </size>
-        </property>
-        <property name="text">
-         <string>min</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="3" column="2">
-       <widget class="QSpinBox" name="spinBoxHeightScaleMin">
-        <property name="maximumSize">
-         <size>
-          <width>80</width>
-          <height>16777215</height>
-         </size>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="maximum">
-         <number>10000</number>
-        </property>
-        <property name="singleStep">
-         <number>100</number>
-        </property>
-       </widget>
-      </item>
-      <item row="3" column="3">
-       <widget class="QLabel" name="label_8">
-        <property name="text">
-         <string>max</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="5" column="2">
-       <widget class="QDoubleSpinBox" name="doubleSpinBoxSlopeScaleMin">
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="decimals">
-         <number>2</number>
-        </property>
-        <property name="maximum">
-         <double>100.000000000000000</double>
-        </property>
-        <property name="singleStep">
-         <double>0.500000000000000</double>
-        </property>
-       </widget>
-      </item>
-      <item row="5" column="3">
-       <widget class="QLabel" name="label_11">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
-          <horstretch>0</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <property name="minimumSize">
-         <size>
-          <width>50</width>
-          <height>20</height>
-         </size>
-        </property>
-        <property name="text">
-         <string>max</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="5" column="6">
-       <widget class="QDoubleSpinBox" name="doubleSpinBoxSlopeScaleMax">
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="decimals">
-         <number>2</number>
-        </property>
-        <property name="maximum">
-         <double>100.000000000000000</double>
-        </property>
-        <property name="singleStep">
-         <double>0.500000000000000</double>
-        </property>
-        <property name="value">
-         <double>1.500000000000000</double>
-        </property>
-       </widget>
-      </item>
-      <item row="6" column="2">
-       <widget class="QDoubleSpinBox" name="doubleSpinBoxCurvatureScaleMin">
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="decimals">
-         <number>2</number>
-        </property>
-        <property name="minimum">
-         <double>-1.000000000000000</double>
-        </property>
-        <property name="maximum">
-         <double>1.000000000000000</double>
-        </property>
-        <property name="singleStep">
-         <double>0.010000000000000</double>
-        </property>
-        <property name="value">
-         <double>-0.100000000000000</double>
-        </property>
-       </widget>
-      </item>
-      <item row="6" column="6">
-       <widget class="QDoubleSpinBox" name="doubleSpinBoxCurvatureScaleMax">
-        <property name="minimumSize">
-         <size>
-          <width>70</width>
-          <height>0</height>
-         </size>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="decimals">
-         <number>2</number>
-        </property>
-        <property name="minimum">
-         <double>-1.000000000000000</double>
-        </property>
-        <property name="maximum">
-         <double>1.000000000000000</double>
-        </property>
-        <property name="singleStep">
-         <double>0.010000000000000</double>
-        </property>
-        <property name="value">
-         <double>0.100000000000000</double>
-        </property>
-       </widget>
-      </item>
-      <item row="6" column="0">
-       <widget class="QLabel" name="label_13">
-        <property name="text">
-         <string>curvature scale</string>
-        </property>
-       </widget>
-      </item>
-      <item row="6" column="1">
-       <widget class="QLabel" name="label_16">
-        <property name="minimumSize">
-         <size>
-          <width>70</width>
-          <height>0</height>
-         </size>
-        </property>
-        <property name="text">
-         <string>min</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="6" column="3">
-       <widget class="QLabel" name="label_17">
-        <property name="text">
-         <string>max</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="10" column="0">
-       <widget class="QLabel" name="label_14">
-        <property name="text">
-         <string>max workers</string>
-        </property>
-       </widget>
-      </item>
-      <item row="10" column="1">
-       <widget class="QSpinBox" name="spinBoxMaxWorkers">
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="minimum">
-         <number>1</number>
-        </property>
-        <property name="maximum">
-         <number>64</number>
-        </property>
-        <property name="value">
-         <number>1</number>
-        </property>
-       </widget>
-      </item>
-      <item row="0" column="0">
-       <widget class="QLabel" name="label_4">
-        <property name="sizePolicy">
-         <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
-          <horstretch>0</horstretch>
-          <verstretch>0</verstretch>
-         </sizepolicy>
-        </property>
-        <property name="text">
-         <string>gaussian filter size</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="0">
-       <widget class="QLabel" name="label_5">
-        <property name="text">
-         <string>gaussian filter sigma</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="2" column="0">
-       <widget class="QLabel" name="label_6">
-        <property name="text">
-         <string>curvature filter size</string>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
-        </property>
-       </widget>
-      </item>
-      <item row="9" column="0">
-       <widget class="QLabel" name="label_3">
-        <property name="text">
-         <string>chunk size</string>
-        </property>
-       </widget>
-      </item>
-      <item row="0" column="1">
-       <widget class="QSpinBox" name="spinBoxGfSize">
-        <property name="layoutDirection">
-         <enum>Qt::LeftToRight</enum>
-        </property>
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="minimum">
-         <number>1</number>
-        </property>
-        <property name="maximum">
-         <number>100</number>
-        </property>
-        <property name="value">
-         <number>12</number>
-        </property>
-       </widget>
-      </item>
-      <item row="1" column="1">
-       <widget class="QSpinBox" name="spinBoxGfSigma">
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="minimum">
-         <number>1</number>
-        </property>
-        <property name="maximum">
-         <number>100</number>
-        </property>
-        <property name="value">
-         <number>3</number>
-        </property>
-       </widget>
-      </item>
-      <item row="2" column="1">
-       <widget class="QSpinBox" name="spinBoxCurvatureSize">
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="minimum">
-         <number>1</number>
-        </property>
-        <property name="maximum">
-         <number>100</number>
-        </property>
-       </widget>
-      </item>
-      <item row="9" column="1">
-       <widget class="QSpinBox" name="spinBoxChunkSize">
-        <property name="alignment">
-         <set>Qt::AlignCenter</set>
-        </property>
-        <property name="minimum">
-         <number>256</number>
-        </property>
-        <property name="maximum">
-         <number>8192</number>
-        </property>
-        <property name="singleStep">
-         <number>256</number>
-        </property>
-        <property name="value">
-         <number>1024</number>
-        </property>
-       </widget>
-      </item>
-     </layout>
-    </widget>
-   </item>
-   <item>
-    <widget class="QCheckBox" name="checkBox_closeAfterProcessing">
-     <property name="mouseTracking">
-      <bool>true</bool>
-     </property>
-     <property name="text">
-      <string>処理終了後，自動でウィンドウを閉じる</string>
-     </property>
-     <property name="iconSize">
-      <size>
-       <width>20</width>
-       <height>20</height>
-      </size>
-     </property>
-    </widget>
-   </item>
-   <item>
-    <layout class="QHBoxLayout" name="horizontalLayout_3">
-     <item>
-      <spacer name="horizontalSpacer">
-       <property name="orientation">
-        <enum>Qt::Horizontal</enum>
-       </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>40</width>
-         <height>20</height>
-        </size>
-       </property>
-      </spacer>
-     </item>
-     <item>
-      <widget class="QPushButton" name="pushButton_cancel">
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>30</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>100</width>
-         <height>30</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>キャンセル</string>
-       </property>
-       <property name="autoDefault">
-        <bool>false</bool>
-       </property>
-      </widget>
-     </item>
-     <item>
-      <widget class="QPushButton" name="pushButton_run">
-       <property name="sizePolicy">
-        <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
-         <horstretch>0</horstretch>
-         <verstretch>0</verstretch>
-        </sizepolicy>
-       </property>
-       <property name="minimumSize">
-        <size>
-         <width>100</width>
-         <height>0</height>
-        </size>
-       </property>
-       <property name="maximumSize">
-        <size>
-         <width>100</width>
-         <height>30</height>
-        </size>
-       </property>
-       <property name="text">
-        <string>実行</string>
-       </property>
-      </widget>
-     </item>
-    </layout>
-   </item>
-  </layout>
- </widget>
- <customwidgets>
-  <customwidget>
-   <class>QgsFileWidget</class>
-   <extends>QWidget</extends>
-   <header>qgsfilewidget.h</header>
-  </customwidget>
- </customwidgets>
- <resources/>
- <connections/>
-</ui>
diff --git a/metadata.txt b/metadata.txt
index 26f43da..97a24c0 100644
--- a/metadata.txt
+++ b/metadata.txt
@@ -8,6 +8,8 @@ version=0.0.1
 #Plugin main icon
 icon=imgs/icon.png
 
+hasProcessingProvider=yes
+
 author=MIERUNE Inc.
 email=info@mierune.co.jp
 homepage=https://github.com/MIERUNE/csmap-qgis-plugin
diff --git a/plugin.py b/plugin.py
new file mode 100644
index 0000000..a8ca4e4
--- /dev/null
+++ b/plugin.py
@@ -0,0 +1,50 @@
+import contextlib
+
+from PyQt5.QtWidgets import QAction, QToolButton
+from qgis._gui import QgisInterface
+from qgis.core import QgsApplication
+
+from .processing_provider.csmap_provider import CSMapProcessingProvider
+
+with contextlib.suppress(ImportError):
+    from processing import execAlgorithmDialog
+
+
+class CSMapPlugin:
+    def __init__(self, iface: QgisInterface):
+        self.iface = iface
+
+    def initGui(self):
+        self.provider = CSMapProcessingProvider()
+        QgsApplication.processingRegistry().addProvider(self.provider)
+
+        if self.iface:
+            self.setup_algorithm_tool_button()
+
+    def unload(self):
+        self.teardown_algorithm_tool_button()
+        QgsApplication.processingRegistry().removeProvider(self.provider)
+
+    def setup_algorithm_tool_button(self):
+        if hasattr(self, "toolButtonAction") and self.toolButtonAction:
+            return
+
+        tool_button = QToolButton()
+        icon = self.provider.icon()
+        default_action = QAction(
+            icon, self.tr("CS立体図を作成"), self.iface.mainWindow()
+        )
+        default_action.triggered.connect(
+            lambda: execAlgorithmDialog("csmap:dem_to_csmap", {})
+        )
+        tool_button.setDefaultAction(default_action)
+
+        self.toolButtonAction = self.iface.addToolBarWidget(tool_button)
+
+    def teardown_algorithm_tool_button(self):
+        if hasattr(self, "toolButtonAction"):
+            self.iface.removeToolBarIcon(self.toolButtonAction)
+            del self.toolButtonAction
+
+    def tr(self, message):
+        return QgsApplication.translate("CS 3D-Map", message)
diff --git a/processing_provider/__init__.py b/processing_provider/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/processing_provider/csmap_algorithm.py b/processing_provider/csmap_algorithm.py
new file mode 100644
index 0000000..8e26b66
--- /dev/null
+++ b/processing_provider/csmap_algorithm.py
@@ -0,0 +1,237 @@
+import multiprocessing
+
+from PyQt5.QtCore import QCoreApplication
+from qgis.core import (
+    QgsProcessingAlgorithm,
+    QgsProcessingParameterNumber,
+    QgsProcessingParameterRasterDestination,
+    QgsProcessingParameterRasterLayer,
+)
+
+from ..csmap_py.csmap import process
+
+
+class CSMapProcessingAlgorithm(QgsProcessingAlgorithm):
+    INPUT = "INPUT"
+    OUTPUT = "OUTPUT"
+    GF_SIZE = "GF_SIZE"
+    GF_SIGMA = "GF_SIGMA"
+    CURVATURE_SIZE = "CURVATURE_SIZE"
+    HEIGHT_SCALE_MIN = "HEIGHT_SCALE_MIN"
+    HEIGHT_SCALE_MAX = "HEIGHT_SCALE_MAX"
+    SLOPE_SCALE_MIN = "SLOPE_SCALE_MIN"
+    SLOPE_SCALE_MAX = "SLOPE_SCALE_MAX"
+    CURVATURE_SCALE_MIN = "CURVATURE_SCALE_MIN"
+    CURVATURE_SCALE_MAX = "CURVATURE_SCALE_MAX"
+    CHUNK_SIZE = "CHUNK_SIZE"
+    MAX_WORKERS = "MAX_WORKERS"
+
+    def initAlgorithm(self, config=None):
+        self.addParameter(
+            QgsProcessingParameterRasterLayer(
+                self.INPUT,
+                self.tr("入力レイヤ（DEM）"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterRasterDestination(
+                self.OUTPUT,
+                self.tr("出力レイヤ"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.GF_SIZE,
+                description="Gaussian Filter Size",
+                type=QgsProcessingParameterNumber.Integer,
+                defaultValue=12,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.GF_SIGMA,
+                description="Gaussian Filter Sigma",
+                type=QgsProcessingParameterNumber.Integer,
+                defaultValue=3,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.CURVATURE_SIZE,
+                description="Curvature Filter Size",
+                type=QgsProcessingParameterNumber.Integer,
+                defaultValue=1,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.HEIGHT_SCALE_MIN,
+                description="Height Scale Min",
+                type=QgsProcessingParameterNumber.Double,
+                defaultValue=0.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.HEIGHT_SCALE_MAX,
+                description="Height Scale Max",
+                type=QgsProcessingParameterNumber.Double,
+                defaultValue=1000.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.SLOPE_SCALE_MIN,
+                description="Slope Scale Min",
+                type=QgsProcessingParameterNumber.Double,
+                defaultValue=0.0,
+                minValue=0.0,
+                maxValue=100.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.SLOPE_SCALE_MAX,
+                description="Slope Scale Max",
+                type=QgsProcessingParameterNumber.Double,
+                defaultValue=1.5,
+                minValue=0.0,
+                maxValue=100.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.CURVATURE_SCALE_MIN,
+                description="Curvature Scale Min",
+                type=QgsProcessingParameterNumber.Double,
+                defaultValue=-0.1,
+                minValue=-1.0,
+                maxValue=1.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.CURVATURE_SCALE_MAX,
+                description="Curvature Scale Max",
+                type=QgsProcessingParameterNumber.Double,
+                defaultValue=0.1,
+                minValue=-1.0,
+                maxValue=1.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.CHUNK_SIZE,
+                description="Chunk Size",
+                type=QgsProcessingParameterNumber.Integer,
+                defaultValue=1024,
+                minValue=256,
+                maxValue=8192,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                name=self.MAX_WORKERS,
+                description="Max Workers",
+                type=QgsProcessingParameterNumber.Integer,
+                defaultValue=multiprocessing.cpu_count(),
+                minValue=1,
+                maxValue=64,
+            )
+        )
+
+    def processAlgorithm(self, parameters, context, feedback):
+        input_layer = self.parameterAsRasterLayer(parameters, self.INPUT, context)
+        output_path = self.parameterAsOutputLayer(parameters, self.OUTPUT, context)
+
+        # parameters
+        gf_size = self.parameterAsInt(parameters, self.GF_SIZE, context)
+        gf_sigma = self.parameterAsInt(parameters, self.GF_SIGMA, context)
+        curvature_size = self.parameterAsInt(parameters, self.CURVATURE_SIZE, context)
+
+        height_scale_min = self.parameterAsDouble(
+            parameters, self.HEIGHT_SCALE_MIN, context
+        )
+        height_scale_max = self.parameterAsDouble(
+            parameters, self.HEIGHT_SCALE_MAX, context
+        )
+
+        slope_scale_min = self.parameterAsDouble(
+            parameters, self.SLOPE_SCALE_MIN, context
+        )
+        slope_scale_max = self.parameterAsDouble(
+            parameters, self.SLOPE_SCALE_MAX, context
+        )
+
+        curvature_scale_min = self.parameterAsDouble(
+            parameters, self.CURVATURE_SCALE_MIN, context
+        )
+        curvature_scale_max = self.parameterAsDouble(
+            parameters, self.CURVATURE_SCALE_MAX, context
+        )
+
+        chunk_size = self.parameterAsInt(parameters, self.CHUNK_SIZE, context)
+        max_workers = self.parameterAsInt(parameters, self.MAX_WORKERS, context)
+
+        params = process.CsmapParams(
+            gf_size=gf_size,
+            gf_sigma=gf_sigma,
+            curvature_size=curvature_size,
+            height_scale=(height_scale_min, height_scale_max),
+            slope_scale=(slope_scale_min, slope_scale_max),
+            curvature_scale=(curvature_scale_min, curvature_scale_max),
+        )
+
+        feedback.pushInfo("DEMを処理中です...")
+
+        try:
+            process.process(
+                input_layer.source(),
+                output_path,
+                chunk_size=chunk_size,
+                params=params,
+                max_workers=max_workers,
+            )
+
+            feedback.pushInfo("処理が正常に完了しました")
+
+        except Exception as e:
+            feedback.reportError(f"処理中にエラーが発生しました: {str(e)}")
+            return {}
+
+        return {self.OUTPUT: output_path}
+
+    def tr(self, string):
+        return QCoreApplication.translate("Processing", string)
+
+    def createInstance(self):
+        return CSMapProcessingAlgorithm()
+
+    def name(self):
+        return "dem_to_csmap"
+
+    def displayName(self):
+        return self.tr("CS立体図を作成（CS 3D-Map）")
+
+    def shortHelpString(self):
+        return self.tr("入力のDEMをGeoTIFF形式のCS立体図に変換します。")
diff --git a/processing_provider/csmap_provider.py b/processing_provider/csmap_provider.py
new file mode 100644
index 0000000..0a0c496
--- /dev/null
+++ b/processing_provider/csmap_provider.py
@@ -0,0 +1,21 @@
+from pathlib import Path
+
+from PyQt5.QtGui import QIcon
+from qgis.core import QgsProcessingProvider
+
+from .csmap_algorithm import CSMapProcessingAlgorithm
+
+
+class CSMapProcessingProvider(QgsProcessingProvider):
+    def loadAlgorithms(self, *args, **kwargs):
+        self.addAlgorithm(CSMapProcessingAlgorithm())
+
+    def id(self, *args, **kwargs):
+        return "csmap"
+
+    def name(self, *args, **kwargs):
+        return self.tr("CSMap Plugin")
+
+    def icon(self):
+        path = (Path(__file__).parent.parent / "imgs" / "icon.png").resolve()
+        return QIcon(str(path))
diff --git a/qcsmap.py b/qcsmap.py
deleted file mode 100644
index 212c65e..0000000
--- a/qcsmap.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import os
-
-from PyQt5.QtWidgets import QAction
-from qgis.PyQt.QtGui import QIcon
-
-from dem_to_csmap import DemToCsMap
-
-PLUGIN_NAME = "CSMap Plugin"
-
-
-class QCsMap:
-    def __init__(self, iface):
-        self.iface = iface
-        self.win = self.iface.mainWindow()
-        self.plugin_dir = os.path.dirname(__file__)
-        self.actions = []
-        self.menu = PLUGIN_NAME
-        self.toolbar = self.iface.addToolBar(PLUGIN_NAME)
-        self.toolbar.setObjectName(PLUGIN_NAME)
-
-    def add_action(
-        self,
-        icon_path,
-        text,
-        callback,
-        enabled_flag=True,
-        add_to_menu=True,
-        add_to_toolbar=True,
-        status_tip=None,
-        whats_this=None,
-        parent=None,
-    ):
-        icon = QIcon(icon_path)
-        action = QAction(icon, text, parent)
-        action.triggered.connect(callback)
-        action.setEnabled(enabled_flag)
-        if status_tip is not None:
-            action.setStatusTip(status_tip)
-        if whats_this is not None:
-            action.setWhatsThis(whats_this)
-        if add_to_toolbar:
-            self.toolbar.addAction(action)
-        if add_to_menu:
-            self.iface.addPluginToMenu(self.menu, action)
-        self.actions.append(action)
-        return action
-
-    def initGui(self):
-        # アイコンのパスを取得
-        icon_path = os.path.join(os.path.dirname(__file__), "imgs", "icon.png")
-        # メニュー設定
-        self.add_action(
-            icon_path=icon_path,
-            text="",
-            callback=self.show_menu_dem_to_csmap,
-            parent=self.win,
-        )
-
-    def unload(self):
-        for action in self.actions:
-            self.iface.removePluginMenu(PLUGIN_NAME, action)
-            self.iface.removeToolBarIcon(action)
-        del self.toolbar
-
-    def show_menu_dem_to_csmap(self):
-        self.show_menu_dem_to_csmap = DemToCsMap()
-        self.show_menu_dem_to_csmap.show()
diff --git a/test_suite.py b/test_suite.py
deleted file mode 100644
index d7cd20d..0000000
--- a/test_suite.py
+++ /dev/null
@@ -1,85 +0,0 @@
-"""
-Test Suite.
-"""
-
-import os
-import sys
-import tempfile
-import unittest
-
-from osgeo import gdal
-from qgis.core import Qgis
-
-try:
-    from pip import main as pipmain
-except ImportError:
-    from pip._internal import main as pipmain
-
-try:
-    import coverage
-except ImportError:
-    pipmain(["install", "coverage"])
-    import coverage
-
-__author__ = "Alessandro Pasotti"
-__revision__ = "$Format:%H$"
-__date__ = "30/04/2018"
-__copyright__ = "Copyright 2018, North Road"
-
-
-def _run_tests(test_suite, package_name, with_coverage=False):
-    """Core function to test a test suite."""
-    count = test_suite.countTestCases()
-    print("########")
-    print("%s tests has been discovered in %s" % (count, package_name))
-    print("Python GDAL : %s" % gdal.VersionInfo("VERSION_NUM"))
-    print("QGIS version : {}".format(Qgis.version()))
-    print("########")
-    if with_coverage:
-        cov = coverage.Coverage(
-            source=["/processing_r"],
-            omit=["*/test/*"],
-        )
-        cov.start()
-
-    unittest.TextTestRunner(verbosity=3, stream=sys.stdout).run(test_suite)
-
-    if with_coverage:
-        cov.stop()
-        cov.save()
-
-        with tempfile.NamedTemporaryFile(delete=False) as report:
-            cov.report(file=report)
-            # Produce HTML reports in the `htmlcov` folder and open index.html
-            # cov.html_report()
-            report.close()
-
-            with open(report.name, "r", encoding="utf8") as fin:
-                print(fin.read())
-
-
-def test_package(package="plugin_dir"):
-    """Test package.
-    This function is called by travis without arguments.
-
-    :param package: The package to test.
-    :type package: str
-    """
-    test_loader = unittest.defaultTestLoader
-    try:
-        test_suite = test_loader.discover(package)
-    except ImportError:
-        test_suite = unittest.TestSuite()
-    _run_tests(test_suite, package)
-
-
-def test_environment():
-    """Test package with an environment variable."""
-    package = os.environ.get("TESTING_PACKAGE", "plugin_dir")
-    test_loader = unittest.defaultTestLoader
-    test_suite = test_loader.discover(package)
-    _run_tests(test_suite, package)
-
-
-if __name__ == "__main__":
-    test_package()
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644
index 63d8e0e..0000000
--- a/tests/__init__.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import os
-import sys
-
-# csmap_pyからcsmapとしてimportするためにパスを追加
-sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__)), "csmap_py"))
diff --git a/tests/fixture/12ke35_1mdem.tif b/tests/fixture/12ke35_1mdem.tif
deleted file mode 100644
index 61337cc..0000000
Binary files a/tests/fixture/12ke35_1mdem.tif and /dev/null differ
diff --git a/tests/fixture/README.md b/tests/fixture/README.md
deleted file mode 100644
index 8777d87..0000000
--- a/tests/fixture/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-## License
-
-- 12ke35_1mdem.tif: <https://www.geospatial.jp/ckan/dataset/h28toumakoukulaser>
diff --git a/tests/qgis_interface.py b/tests/qgis_interface.py
deleted file mode 100644
index bed572e..0000000
--- a/tests/qgis_interface.py
+++ /dev/null
@@ -1,225 +0,0 @@
-# coding=utf-8
-"""QGIS plugin implementation.
-
-.. note:: This program is free software; you can redistribute it and/or modify
-     it under the terms of the GNU General Public License as published by
-     the Free Software Foundation; either version 2 of the License, or
-     (at your option) any later version.
-
-.. note:: This source code was copied from the 'postgis viewer' application
-     with original authors:
-     Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk
-     Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org
-     Copyright (c) 2014 Tim Sutton, tim@linfiniti.com
-
-     reference: https://github.com/felt/qgis-plugin/blob/main/felt/test/qgis_interface.py
-"""
-
-__author__ = "tim@linfiniti.com"
-__revision__ = "$Format:%H$"
-__date__ = "10/01/2011"
-__copyright__ = (
-    "Copyright (c) 2010 by Ivan Mincik, ivan.mincik@gista.sk and "
-    "Copyright (c) 2011 German Carrillo, geotux_tuxman@linuxmail.org"
-    "Copyright (c) 2014 Tim Sutton, tim@linfiniti.com"
-)
-
-from typing import List
-
-from PyQt5.QtCore import QObject, QSize, pyqtSignal, pyqtSlot
-from qgis.core import QgsMapLayer, QgsProject
-from qgis.gui import QgsMapCanvas, QgsMessageBar
-from qgis.PyQt.QtWidgets import QDockWidget
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class QgisInterface(QObject):
-    """Class to expose QGIS objects and functions to plugins.
-
-    This class is here for enabling us to run unit tests only,
-    so most methods are simply stubs.
-    """
-
-    currentLayerChanged = pyqtSignal(QgsMapLayer)
-
-    def __init__(self, canvas: QgsMapCanvas):
-        """Constructor
-        :param canvas:
-        """
-        QObject.__init__(self)
-        self.canvas = canvas
-        # Set up slots so we can mimic the behaviour of QGIS when layers
-        # are added.
-
-        # noinspection PyArgumentList
-        QgsProject.instance().layersAdded.connect(self.addLayers)
-        # noinspection PyArgumentList
-        QgsProject.instance().layerWasAdded.connect(self.addLayer)
-        # noinspection PyArgumentList
-        QgsProject.instance().removeAll.connect(self.removeAllLayers)
-
-        # For processing module
-        self.destCrs = None
-
-        self.message_bar = QgsMessageBar()
-
-    def addLayers(self, layers: List[QgsMapLayer]):
-        """Handle layers being added to the registry so they show up in canvas.
-
-        :param layers: list<QgsMapLayer> list of map layers that were added
-
-        .. note:: The QgsInterface api does not include this method,
-            it is added here as a helper to facilitate testing.
-        """
-        # LOGGER.debug('addLayers called on qgis_interface')
-        # LOGGER.debug('Number of layers being added: %s' % len(layers))
-        # LOGGER.debug('Layer Count Before: %s' % len(self.canvas.layers()))
-        current_layers = self.canvas.layers()
-        final_layers = []
-        for layer in current_layers:
-            final_layers.append(layer)
-        for layer in layers:
-            final_layers.append(layer)
-
-        self.canvas.setLayers(final_layers)
-        # LOGGER.debug('Layer Count After: %s' % len(self.canvas.layers()))
-
-    def addLayer(self, layer: QgsMapLayer):
-        """Handle a layer being added to the registry so it shows up in canvas.
-
-        :param layer: list<QgsMapLayer> list of map layers that were added
-
-        .. note: The QgsInterface api does not include this method, it is added
-                 here as a helper to facilitate testing.
-
-        .. note: The addLayer method was deprecated in QGIS 1.8 so you should
-                 not need this method much.
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    @pyqtSlot()
-    def removeAllLayers(self):  # pylint: disable=no-self-use
-        """Remove layers from the canvas before they get deleted."""
-        self.canvas.setLayers([])
-
-    def newProject(self):  # pylint: disable=no-self-use
-        """Create new project."""
-        # noinspection PyArgumentList
-        QgsProject.instance().clear()
-
-    # ---------------- API Mock for QgsInterface follows -------------------
-
-    def zoomFull(self):
-        """Zoom to the map full extent."""
-        pass  # pylint: disable=unnecessary-pass
-
-    def zoomToPrevious(self):
-        """Zoom to previous view extent."""
-        pass  # pylint: disable=unnecessary-pass
-
-    def zoomToNext(self):
-        """Zoom to next view extent."""
-        pass  # pylint: disable=unnecessary-pass
-
-    def zoomToActiveLayer(self):
-        """Zoom to extent of active layer."""
-        pass  # pylint: disable=unnecessary-pass
-
-    def addVectorLayer(self, path: str, base_name: str, provider_key: str):
-        """Add a vector layer.
-
-        :param path: Path to layer.
-        :type path: str
-
-        :param base_name: Base name for layer.
-        :type base_name: str
-
-        :param provider_key: Provider key e.g. 'ogr'
-        :type provider_key: str
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    def addRasterLayer(self, path: str, base_name: str):
-        """Add a raster layer given a raster layer file name
-
-        :param path: Path to layer.
-        :type path: str
-
-        :param base_name: Base name for layer.
-        :type base_name: str
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    def activeLayer(self) -> QgsMapLayer:  # pylint: disable=no-self-use
-        """Get pointer to the active layer (layer selected in the legend)."""
-        # noinspection PyArgumentList
-        layers = QgsProject.instance().mapLayers()
-        for item in layers:
-            return layers[item]
-
-    def addToolBarIcon(self, action):
-        """Add an icon to the plugins toolbar.
-
-        :param action: Action to add to the toolbar.
-        :type action: QAction
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    def removeToolBarIcon(self, action):
-        """Remove an action (icon) from the plugin toolbar.
-
-        :param action: Action to add to the toolbar.
-        :type action: QAction
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    def addToolBar(self, name):
-        """Add toolbar with specified name.
-
-        :param name: Name for the toolbar.
-        :type name: str
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    def mapCanvas(self) -> QgsMapCanvas:
-        """Return a pointer to the map canvas."""
-        return self.canvas
-
-    def mainWindow(self):
-        """Return a pointer to the main window.
-
-        In case of QGIS it returns an instance of QgisApp.
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    def addDockWidget(self, area, dock_widget: QDockWidget):
-        """Add a dock widget to the main window.
-
-        :param area: Where in the ui the dock should be placed.
-        :type area:
-
-        :param dock_widget: A dock widget to add to the UI.
-        :type dock_widget: QDockWidget
-        """
-        pass  # pylint: disable=unnecessary-pass
-
-    def legendInterface(self):
-        """Get the legend."""
-        return self.canvas
-
-    def iconSize(self, dockedToolbar) -> int:
-        """
-        Returns the toolbar icon size.
-        :param dockedToolbar: If True, the icon size
-        for toolbars contained within docks is returned.
-        """
-        if dockedToolbar:
-            return QSize(16, 16)
-
-        return QSize(24, 24)
-
-    def messageBar(self) -> QgsMessageBar:
-        """
-        Return the message bar of the main app
-        """
-        return self.message_bar
diff --git a/tests/test_menu.py b/tests/test_menu.py
deleted file mode 100644
index ac66ddb..0000000
--- a/tests/test_menu.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import os
-import unittest
-
-from qgis.core import QgsProject
-
-from dem_to_csmap import DemToCsMap
-
-from .utilities import get_qgis_app
-
-QGIS_APP, CANVAS, IFACE, PARENT = get_qgis_app()
-
-
-class TestMenu(unittest.TestCase):
-    def test_menu(self):
-        menu = DemToCsMap()
-
-        assert menu.isVisible() is False
-        menu.show()
-        assert menu.isVisible() is True
-        menu.hide()
-        assert menu.isVisible() is False
-
-        menu.mQgsFileWidget_input.setFilePath(
-            os.path.join(os.path.dirname(__file__), "fixture", "12ke35_1mdem.tif")
-        )
-
-        filename = "_result.tif"
-        output_path = os.path.join(os.path.dirname(__file__), filename)
-        menu.mQgsFileWidget_output.setFilePath(output_path)
-
-        menu.pushButton_run.click()  # run process
-
-        assert os.path.exists(output_path) is True
-        assert len(QgsProject.instance().mapLayersByName(filename)) == 1
-
-
-if __name__ == "__main__":
-    unittest.main()
diff --git a/tests/utilities.py b/tests/utilities.py
deleted file mode 100644
index 3db60f0..0000000
--- a/tests/utilities.py
+++ /dev/null
@@ -1,112 +0,0 @@
-"""
-reference: https://github.com/felt/qgis-plugin/blob/main/felt/test/utilities.py
-Common functionality used by regression tests.
-"""
-
-import atexit
-import logging
-import os
-import sys
-
-from qgis.core import QgsApplication
-from qgis.gui import QgsMapCanvas
-from qgis.PyQt.QtCore import QSize
-from qgis.PyQt.QtWidgets import QWidget
-from qgis.utils import iface
-
-from .qgis_interface import QgisInterface
-
-LOGGER = logging.getLogger("QGIS")
-QGIS_APP = None  # Static variable used to hold hand to running QGIS app
-CANVAS = None
-PARENT = None
-IFACE = None
-
-
-def get_qgis_app(cleanup=True):
-    """Start one QGIS application to test against.
-
-    :returns: Handle to QGIS app, canvas, iface and parent. If there are any
-        errors the tuple members will be returned as None.
-    :rtype: (QgsApplication, CANVAS, IFACE, PARENT)
-
-    If QGIS is already running the handle to that app will be returned.
-    """
-
-    global QGIS_APP, PARENT, IFACE, CANVAS  # pylint: disable=W0603
-
-    if iface:
-        QGIS_APP = QgsApplication
-        CANVAS = iface.mapCanvas()
-        PARENT = iface.mainWindow()
-        IFACE = iface
-        return QGIS_APP, CANVAS, IFACE, PARENT
-
-    global QGISAPP  # pylint: disable=global-variable-undefined
-
-    try:
-        QGISAPP  # pylint: disable=used-before-assignment
-    except NameError:
-        myGuiFlag = True  # All test will run qgis in gui mode
-
-        # In python3 we need to convert to a bytes object (or should
-        # QgsApplication accept a QString instead of const char* ?)
-        try:
-            argvb = list(map(os.fsencode, sys.argv))
-        except AttributeError:
-            argvb = sys.argv
-
-        # Note: QGIS_PREFIX_PATH is evaluated in QgsApplication -
-        # no need to mess with it here.
-        QGISAPP = QgsApplication(argvb, myGuiFlag)
-
-        QGISAPP.initQgis()
-        s = QGISAPP.showSettings()
-        LOGGER.debug(s)
-
-        def debug_log_message(message, tag, level):
-            """
-            Prints a debug message to a log
-            :param message: message to print
-            :param tag: log tag
-            :param level: log message level (severity)
-            :return:
-            """
-            print("{}({}): {}".format(tag, level, message))
-
-        QgsApplication.instance().messageLog().messageReceived.connect(
-            debug_log_message
-        )
-
-        if cleanup:
-
-            @atexit.register
-            def exitQgis():  # pylint: disable=unused-variable
-                """
-                Gracefully closes the QgsApplication instance
-                """
-                try:
-                    # pylint: disable=used-before-assignment
-                    # pylint: disable=redefined-outer-name
-                    QGISAPP.exitQgis()  # noqa
-                    QGISAPP = None  # noqa
-                    # pylint: enable=used-before-assignment
-                    # pylint: enable=redefined-outer-name
-                except NameError:
-                    pass
-
-    if PARENT is None:
-        # noinspection PyPep8Naming
-        PARENT = QWidget()
-
-    if CANVAS is None:
-        # noinspection PyPep8Naming
-        CANVAS = QgsMapCanvas(PARENT)
-        CANVAS.resize(QSize(400, 400))
-
-    if IFACE is None:
-        # QgisInterface is a stub implementation of the QGIS plugin interface
-        # noinspection PyPep8Naming
-        IFACE = QgisInterface(CANVAS)
-
-    return QGISAPP, CANVAS, IFACE, PARENT


Reviews:
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This PR refactors the CSMap plugin by phasing out legacy UI components and transitioning the DEM processing functionality into QGIS’s native processing framework. Key changes include:
- Removal of legacy files (qcsmap.py, dem_to_csmap.py, and associated UI) to deprecate the old workflow.
- Addition of a new processing provider and algorithm (in processing_provider/csmap_provider.py and processing_provider/csmap_algorithm.py) to integrate with QGIS processing.
- Updates in the plugin entrypoint (__init__.py and plugin.py) to register and utilize the new processing-based implementation.

### Reviewed Changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated no comments.

<details>
<summary>Show a summary per file</summary>

| File                                        | Description                                                     |
| ------------------------------------------- | --------------------------------------------------------------- |
| qcsmap.py                                   | Removed legacy plugin implementation.                           |
| processing_provider/csmap_provider.py         | Introduces a new processing provider for CSMap.                 |
| processing_provider/csmap_algorithm.py          | Implements the processing algorithm for DEM conversion.         |
| plugin.py                                   | Updates plugin initialization to use the processing provider.    |
| dem_to_csmap.py                             | Removes the legacy DEM processing dialog implementation.         |
| csmap_py/csmap/process.py                     | Adjusts import statements for relative module consistency.         |
| __init__.py                                 | Updates the plugin entrypoint to register CSMapPlugin.            |
</details>


<details>
<summary>Files not reviewed (1)</summary>

* **metadata.txt**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (1)</summary>

**processing_provider/csmap_algorithm.py:255**
* [nitpick] The algorithm's name is hardcoded as 'dem_to_csmap', which might be confusing given the new processing provider is identified as 'csmap'. Consider reviewing the naming conventions to ensure clarity and consistency across the provider and algorithm identifiers.
```
def name(self):
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-01T04:46:24Z
user: login: copilot-pull-request-reviewer[bot]
body: ## Pull Request Overview

This pull request refactors the legacy DEM-to-CSMap functionality by removing the old UI‐based implementation and related tests, replacing them with a new processing provider algorithm.  
- Removed legacy UI code (dem_to_csmap.py) and associated tests.  
- Introduced a processing provider and algorithm (CSMapProcessingProvider and CSMapProcessingAlgorithm) for DEM processing.  
- Updated plugin initialization (__init__.py and plugin.py) to align with the new processing framework.

### Reviewed Changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated no comments.

<details>
<summary>Show a summary per file</summary>

| File                                   | Description                                                                        |
| -------------------------------------- | ---------------------------------------------------------------------------------- |
| tests/utilities.py                     | Removed legacy test utilities; verify that necessary test coverage is maintained.   |
| tests/test_menu.py                     | Removed UI tests specific to the old dem_to_csmap implementation; ensure coverage for new provider functionality. |
| tests/qgis_interface.py                 | Deleted stub for QGIS interface testing; confirm that new testing adequately covers plugin interface behavior. |
| tests/fixture/README.md                | Removed unused fixture documentation.                                            |
| tests/__init__.py                      | Removed initialization file for tests; ensure new test suite configuration is complete. |
| test_suite.py                          | Removed legacy test suite; verify current tests cover updated functionality.       |
| qcsmap.py                              | Removed legacy plugin UI; functionality replaced by processing provider method.      |
| processing_provider/csmap_provider.py  | Added new processing provider for CSMap functionality.                           |
| processing_provider/csmap_algorithm.py | Added new algorithm implementation with updated parameter setup and processing logic. |
| plugin.py                              | Introduces processing provider registration and a new tool button for algorithm execution. |
| __init__.py                            | Adjusted plugin entry point to load CSMapPlugin and update sys.path accordingly.       |
| .github/workflows/test.yaml            | Updated CI workflow by removing legacy docker test commands; ensure tests run as expected. |
</details>


<details>
<summary>Files not reviewed (1)</summary>

* **metadata.txt**: Language not supported
</details>

<details>
<summary>Comments suppressed due to low confidence (3)</summary>

**tests/utilities.py:1**
* The removal of this test utility file may result in reduced test coverage for QGIS application initialization. Please ensure that the new processing provider has adequate tests covering its initialization and behavior.
```
   -"""
```
**tests/test_menu.py:1**
* Removing the UI tests for the DemToCsMap widget could lead to insufficient test coverage for the processing functionality replacement. Confirm that tests for the new processing provider and related UI interactions are implemented.
```
   -import os
```
**processing_provider/csmap_algorithm.py:255**
* [nitpick] The algorithm's name method returns 'dem_to_csmap', which might be inconsistent with the new processing provider context and the display name provided. Consider renaming it for consistency with the provider id and displayName.
```
    def name(self):
```
</details>


state: COMMENTED
author_association: NONE
submitted_at: 2025-04-02T07:20:29Z
user: login: nbayashi
body: 残ってるコメント消していいかと思いますのでご確認ください
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-08T00:48:38Z
user: login: nbayashi
body: 細かい点だけコメントしました。対応するかどうかは任せます。
対応しない場合はそのまま上森さんの方でマージしても大丈夫です
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-10T07:41:09Z
user: login: Geo-Jagaimo
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-10T08:09:21Z
user: login: nbayashi
body: 良いと思います
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-14T03:00:08Z

Review comments:
diff_hunk: @@ -0,0 +1,262 @@
+import multiprocessing
+import os
+
+from PyQt5.QtCore import QCoreApplication
+from qgis.core import (
+    QgsProcessingAlgorithm,
+    QgsProcessingParameterBoolean,
+    QgsProcessingParameterFileDestination,
+    QgsProcessingParameterNumber,
+    QgsProcessingParameterRasterLayer,
+    QgsProject,
+    QgsRasterLayer,
+)
+
+from ..csmap_py.csmap import process
+
+
+class CSMapProcessingAlgorithm(QgsProcessingAlgorithm):
+    INPUT = "INPUT"
+    OUTPUT = "OUTPUT"
+    GF_SIZE = "GF_SIZE"
+    GF_SIGMA = "GF_SIGMA"
+    CURVATURE_SIZE = "CURVATURE_SIZE"
+    HEIGHT_SCALE_MIN = "HEIGHT_SCALE_MIN"
+    HEIGHT_SCALE_MAX = "HEIGHT_SCALE_MAX"
+    SLOPE_SCALE_MIN = "SLOPE_SCALE_MIN"
+    SLOPE_SCALE_MAX = "SLOPE_SCALE_MAX"
+    CURVATURE_SCALE_MIN = "CURVATURE_SCALE_MIN"
+    CURVATURE_SCALE_MAX = "CURVATURE_SCALE_MAX"
+    CHUNK_SIZE = "CHUNK_SIZE"
+    MAX_WORKERS = "MAX_WORKERS"
+    LOAD_RESULT = "LOAD_RESULT"
+
+    def initAlgorithm(self, config=None):
+        self.addParameter(
+            QgsProcessingParameterRasterLayer(
+                self.INPUT,
+                self.tr("入力レイヤ（DEM）"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterFileDestination(
+                self.OUTPUT,
+                self.tr("出力レイヤ"),
+                "GeoTIFF files(*.tif *.tiff *.TIF *.TIFF)",
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIZE,
+                "Gaussian Filter Size",
+                QgsProcessingParameterNumber.Integer,
+                12,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIGMA,
+                "Gaussian Filter Sigma",
+                QgsProcessingParameterNumber.Integer,
+                3,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SIZE,
+                "Curvature Size",
+                QgsProcessingParameterNumber.Integer,
+                1,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.HEIGHT_SCALE_MIN,
+                "Height Scale Min",
+                QgsProcessingParameterNumber.Double,
+                0.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.HEIGHT_SCALE_MAX,
+                "Height Scale Max",
+                QgsProcessingParameterNumber.Double,
+                1000.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.SLOPE_SCALE_MIN,
+                "Slope Scale Min",
+                QgsProcessingParameterNumber.Double,
+                0.0,
+                minValue=0.0,
+                maxValue=100.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.SLOPE_SCALE_MAX,
+                "Slope Scale Max",
+                QgsProcessingParameterNumber.Double,
+                1.5,
+                minValue=0.0,
+                maxValue=100.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SCALE_MIN,
+                "Curvature Scale Min",
+                QgsProcessingParameterNumber.Double,
+                -0.1,
+                minValue=-1.0,
+                maxValue=1.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SCALE_MAX,
+                "Curvature Scale Max",
+                QgsProcessingParameterNumber.Double,
+                0.1,
+                minValue=-1.0,
+                maxValue=1.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CHUNK_SIZE,
+                "Chunk Size",
+                QgsProcessingParameterNumber.Integer,
+                1024,
+                minValue=256,
+                maxValue=8192,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.MAX_WORKERS,
+                "Max Workers",
+                QgsProcessingParameterNumber.Integer,
+                multiprocessing.cpu_count(),
+                minValue=1,
+                maxValue=64,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterBoolean(
+                self.LOAD_RESULT,
+                "アルゴリズムの終了後に出力ファイルを開く",
+                defaultValue=True,
+            )
+        )
+
+    def processAlgorithm(self, parameters, context, feedback):
+        """
+        Here is where the processing itself takes place.
+        """
+
path: processing_provider/csmap_algorithm.py
user: login: nbayashi
body: ここのコメントは削除してもいいかも
created_at: 2025-04-08T00:35:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,262 @@
+import multiprocessing
+import os
+
+from PyQt5.QtCore import QCoreApplication
+from qgis.core import (
+    QgsProcessingAlgorithm,
+    QgsProcessingParameterBoolean,
+    QgsProcessingParameterFileDestination,
+    QgsProcessingParameterNumber,
+    QgsProcessingParameterRasterLayer,
+    QgsProject,
+    QgsRasterLayer,
+)
+
+from ..csmap_py.csmap import process
+
+
+class CSMapProcessingAlgorithm(QgsProcessingAlgorithm):
+    INPUT = "INPUT"
+    OUTPUT = "OUTPUT"
+    GF_SIZE = "GF_SIZE"
+    GF_SIGMA = "GF_SIGMA"
+    CURVATURE_SIZE = "CURVATURE_SIZE"
+    HEIGHT_SCALE_MIN = "HEIGHT_SCALE_MIN"
+    HEIGHT_SCALE_MAX = "HEIGHT_SCALE_MAX"
+    SLOPE_SCALE_MIN = "SLOPE_SCALE_MIN"
+    SLOPE_SCALE_MAX = "SLOPE_SCALE_MAX"
+    CURVATURE_SCALE_MIN = "CURVATURE_SCALE_MIN"
+    CURVATURE_SCALE_MAX = "CURVATURE_SCALE_MAX"
+    CHUNK_SIZE = "CHUNK_SIZE"
+    MAX_WORKERS = "MAX_WORKERS"
+    LOAD_RESULT = "LOAD_RESULT"
+
+    def initAlgorithm(self, config=None):
+        self.addParameter(
+            QgsProcessingParameterRasterLayer(
+                self.INPUT,
+                self.tr("入力レイヤ（DEM）"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterFileDestination(
+                self.OUTPUT,
+                self.tr("出力レイヤ"),
+                "GeoTIFF files(*.tif *.tiff *.TIF *.TIFF)",
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIZE,
+                "Gaussian Filter Size",
+                QgsProcessingParameterNumber.Integer,
+                12,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIGMA,
+                "Gaussian Filter Sigma",
+                QgsProcessingParameterNumber.Integer,
+                3,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SIZE,
+                "Curvature Size",
+                QgsProcessingParameterNumber.Integer,
+                1,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.HEIGHT_SCALE_MIN,
+                "Height Scale Min",
+                QgsProcessingParameterNumber.Double,
+                0.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.HEIGHT_SCALE_MAX,
+                "Height Scale Max",
+                QgsProcessingParameterNumber.Double,
+                1000.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.SLOPE_SCALE_MIN,
+                "Slope Scale Min",
+                QgsProcessingParameterNumber.Double,
+                0.0,
+                minValue=0.0,
+                maxValue=100.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.SLOPE_SCALE_MAX,
+                "Slope Scale Max",
+                QgsProcessingParameterNumber.Double,
+                1.5,
+                minValue=0.0,
+                maxValue=100.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SCALE_MIN,
+                "Curvature Scale Min",
+                QgsProcessingParameterNumber.Double,
+                -0.1,
+                minValue=-1.0,
+                maxValue=1.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SCALE_MAX,
+                "Curvature Scale Max",
+                QgsProcessingParameterNumber.Double,
+                0.1,
+                minValue=-1.0,
+                maxValue=1.0,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CHUNK_SIZE,
+                "Chunk Size",
+                QgsProcessingParameterNumber.Integer,
+                1024,
+                minValue=256,
+                maxValue=8192,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.MAX_WORKERS,
+                "Max Workers",
+                QgsProcessingParameterNumber.Integer,
+                multiprocessing.cpu_count(),
+                minValue=1,
+                maxValue=64,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterBoolean(
+                self.LOAD_RESULT,
+                "アルゴリズムの終了後に出力ファイルを開く",
+                defaultValue=True,
+            )
+        )
+
+    def processAlgorithm(self, parameters, context, feedback):
+        """
+        Here is where the processing itself takes place.
+        """
+
+        input_layer = self.parameterAsRasterLayer(parameters, self.INPUT, context)
+        output_path = self.parameterAsFileOutput(parameters, self.OUTPUT, context)
+
+        gf_size = self.parameterAsInt(parameters, self.GF_SIZE, context)
+        gf_sigma = self.parameterAsInt(parameters, self.GF_SIGMA, context)
+        curvature_size = self.parameterAsInt(parameters, self.CURVATURE_SIZE, context)
+
+        height_scale_min = self.parameterAsDouble(
+            parameters, self.HEIGHT_SCALE_MIN, context
+        )
+        height_scale_max = self.parameterAsDouble(
+            parameters, self.HEIGHT_SCALE_MAX, context
+        )
+
+        slope_scale_min = self.parameterAsDouble(
+            parameters, self.SLOPE_SCALE_MIN, context
+        )
+        slope_scale_max = self.parameterAsDouble(
+            parameters, self.SLOPE_SCALE_MAX, context
+        )
+
+        curvature_scale_min = self.parameterAsDouble(
+            parameters, self.CURVATURE_SCALE_MIN, context
+        )
+        curvature_scale_max = self.parameterAsDouble(
+            parameters, self.CURVATURE_SCALE_MAX, context
+        )
+
+        chunk_size = self.parameterAsInt(parameters, self.CHUNK_SIZE, context)
+        max_workers = self.parameterAsInt(parameters, self.MAX_WORKERS, context)
+
+        load_result = self.parameterAsBool(parameters, self.LOAD_RESULT, context)
+
+        params = process.CsmapParams(
+            gf_size=gf_size,
+            gf_sigma=gf_sigma,
+            curvature_size=curvature_size,
+            height_scale=(height_scale_min, height_scale_max),
+            slope_scale=(slope_scale_min, slope_scale_max),
+            curvature_scale=(curvature_scale_min, curvature_scale_max),
+        )
+
+        feedback.pushInfo("DEMを処理中です...")
+
+        try:
+            process.process(
+                input_layer.source(),
+                output_path,
+                chunk_size=chunk_size,
+                params=params,
+                max_workers=max_workers,
+            )
+
+            feedback.pushInfo("処理が正常に完了しました")
+
+            if load_result and context.project() is not None:
+                rlayer = QgsRasterLayer(output_path, os.path.basename(output_path))
+                QgsProject.instance().addMapLayer(rlayer)
+
+        except Exception as e:
+            feedback.reportError(f"処理中にエラーが発生しました: {str(e)}")
+            return {}
+
+        return {self.OUTPUT: output_path}
+
+    def tr(self, string):
+        """
+        Returns a translatable string with the self.tr() function.
+        """
path: processing_provider/csmap_algorithm.py
user: login: nbayashi
body: ここもコメントが残っているようなので削除
created_at: 2025-04-08T00:46:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,42 @@
+import contextlib
+
+from PyQt5.QtWidgets import QAction, QToolButton
+from qgis._gui import QgisInterface
+from qgis.core import QgsApplication
+
+from .processing_provider.csmap_provider import CSMapProcessingProvider
+
+with contextlib.suppress(ImportError):
+    from processing import execAlgorithmDialog
+
+
+class CSMapPlugin:
+    def __init__(self, iface: QgisInterface):
+        self.iface = iface
+
+    def initGui(self):
+        self.provider = CSMapProcessingProvider()
+        QgsApplication.processingRegistry().addProvider(self.provider)
+
+        if self.iface:
+            self.setup_algorithms_tool_button()
+
+    def unload(self):
+        QgsApplication.processingRegistry().removeProvider(self.provider)
+
+    def setup_algorithms_tool_button(self):
+        """
+        Add a button to call the algorithm on the toolbar.
+        """
path: plugin.py
user: login: nbayashi
body: ここは雛形のコメントが残ってる感じなので削除でいいかと

created_at: 2025-04-08T00:47:24Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,237 @@
+import multiprocessing
+
+from PyQt5.QtCore import QCoreApplication
+from qgis.core import (
+    QgsProcessingAlgorithm,
+    QgsProcessingParameterNumber,
+    QgsProcessingParameterRasterDestination,
+    QgsProcessingParameterRasterLayer,
+)
+
+from ..csmap_py.csmap import process
+
+
+class CSMapProcessingAlgorithm(QgsProcessingAlgorithm):
+    INPUT = "INPUT"
+    OUTPUT = "OUTPUT"
+    GF_SIZE = "GF_SIZE"
+    GF_SIGMA = "GF_SIGMA"
+    CURVATURE_SIZE = "CURVATURE_SIZE"
+    HEIGHT_SCALE_MIN = "HEIGHT_SCALE_MIN"
+    HEIGHT_SCALE_MAX = "HEIGHT_SCALE_MAX"
+    SLOPE_SCALE_MIN = "SLOPE_SCALE_MIN"
+    SLOPE_SCALE_MAX = "SLOPE_SCALE_MAX"
+    CURVATURE_SCALE_MIN = "CURVATURE_SCALE_MIN"
+    CURVATURE_SCALE_MAX = "CURVATURE_SCALE_MAX"
+    CHUNK_SIZE = "CHUNK_SIZE"
+    MAX_WORKERS = "MAX_WORKERS"
+
+    def initAlgorithm(self, config=None):
+        self.addParameter(
+            QgsProcessingParameterRasterLayer(
+                self.INPUT,
+                self.tr("入力レイヤ（DEM）"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterRasterDestination(
+                self.OUTPUT,
+                self.tr("出力レイヤ"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIZE,
+                "Gaussian Filter Size",
+                QgsProcessingParameterNumber.Integer,
+                12,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIGMA,
+                "Gaussian Filter Sigma",
+                QgsProcessingParameterNumber.Integer,
+                3,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SIZE,
+                "Curvature Size",
path: processing_provider/csmap_algorithm.py
user: login: nbayashi
body: 元のプラグインではここの表記は「curvature **filter** size」となっていますが、合わせなくて良いですか？
created_at: 2025-04-10T07:00:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,237 @@
+import multiprocessing
+
+from PyQt5.QtCore import QCoreApplication
+from qgis.core import (
+    QgsProcessingAlgorithm,
+    QgsProcessingParameterNumber,
+    QgsProcessingParameterRasterDestination,
+    QgsProcessingParameterRasterLayer,
+)
+
+from ..csmap_py.csmap import process
+
+
+class CSMapProcessingAlgorithm(QgsProcessingAlgorithm):
+    INPUT = "INPUT"
+    OUTPUT = "OUTPUT"
+    GF_SIZE = "GF_SIZE"
+    GF_SIGMA = "GF_SIGMA"
+    CURVATURE_SIZE = "CURVATURE_SIZE"
+    HEIGHT_SCALE_MIN = "HEIGHT_SCALE_MIN"
+    HEIGHT_SCALE_MAX = "HEIGHT_SCALE_MAX"
+    SLOPE_SCALE_MIN = "SLOPE_SCALE_MIN"
+    SLOPE_SCALE_MAX = "SLOPE_SCALE_MAX"
+    CURVATURE_SCALE_MIN = "CURVATURE_SCALE_MIN"
+    CURVATURE_SCALE_MAX = "CURVATURE_SCALE_MAX"
+    CHUNK_SIZE = "CHUNK_SIZE"
+    MAX_WORKERS = "MAX_WORKERS"
+
+    def initAlgorithm(self, config=None):
+        self.addParameter(
+            QgsProcessingParameterRasterLayer(
+                self.INPUT,
+                self.tr("入力レイヤ（DEM）"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterRasterDestination(
+                self.OUTPUT,
+                self.tr("出力レイヤ"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIZE,
+                "Gaussian Filter Size",
+                QgsProcessingParameterNumber.Integer,
+                12,
path: processing_provider/csmap_algorithm.py
user: login: nbayashi
body: ```
name=self.GF_SIZE,
description="Gaussian Filter Size",
type=QgsProcessingParameterNumber.Integer,
defaultValue=12,
```

とすると丁寧。以下全てのパラメータも同様。
created_at: 2025-04-10T07:31:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,237 @@
+import multiprocessing
+
+from PyQt5.QtCore import QCoreApplication
+from qgis.core import (
+    QgsProcessingAlgorithm,
+    QgsProcessingParameterNumber,
+    QgsProcessingParameterRasterDestination,
+    QgsProcessingParameterRasterLayer,
+)
+
+from ..csmap_py.csmap import process
+
+
+class CSMapProcessingAlgorithm(QgsProcessingAlgorithm):
+    INPUT = "INPUT"
+    OUTPUT = "OUTPUT"
+    GF_SIZE = "GF_SIZE"
+    GF_SIGMA = "GF_SIGMA"
+    CURVATURE_SIZE = "CURVATURE_SIZE"
+    HEIGHT_SCALE_MIN = "HEIGHT_SCALE_MIN"
+    HEIGHT_SCALE_MAX = "HEIGHT_SCALE_MAX"
+    SLOPE_SCALE_MIN = "SLOPE_SCALE_MIN"
+    SLOPE_SCALE_MAX = "SLOPE_SCALE_MAX"
+    CURVATURE_SCALE_MIN = "CURVATURE_SCALE_MIN"
+    CURVATURE_SCALE_MAX = "CURVATURE_SCALE_MAX"
+    CHUNK_SIZE = "CHUNK_SIZE"
+    MAX_WORKERS = "MAX_WORKERS"
+
+    def initAlgorithm(self, config=None):
+        self.addParameter(
+            QgsProcessingParameterRasterLayer(
+                self.INPUT,
+                self.tr("入力レイヤ（DEM）"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterRasterDestination(
+                self.OUTPUT,
+                self.tr("出力レイヤ"),
+                optional=False,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIZE,
+                "Gaussian Filter Size",
+                QgsProcessingParameterNumber.Integer,
+                12,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.GF_SIGMA,
+                "Gaussian Filter Sigma",
+                QgsProcessingParameterNumber.Integer,
+                3,
+                minValue=1,
+                maxValue=100,
+            )
+        )
+
+        self.addParameter(
+            QgsProcessingParameterNumber(
+                self.CURVATURE_SIZE,
+                "Curvature Size",
path: processing_provider/csmap_algorithm.py
user: login: Geo-Jagaimo
body: 修正します。ありがとうございます。
created_at: 2025-04-10T08:09:21Z
author_association: MEMBER

PR comments:
user: login: Geo-Jagaimo
created_at: 2025-04-02T07:28:49Z
body: @nbayashi 

レビューをお願いします！
author_association: MEMBER
user: login: nbayashi
created_at: 2025-04-08T00:26:08Z
body: 直接プロセシング化と関係ないかもしれませんが、プラグインの管理で、チェックを消したり入れたりすると、ツールバーのアイコンが無限に増えてしまいます。

https://github.com/user-attachments/assets/bb6e6002-70c9-43fb-99e0-2d83e64dbfef




author_association: CONTRIBUTOR
user: login: nbayashi
created_at: 2025-04-08T00:53:09Z
body: アルゴリズムの終了後に出力ファイルを開くのチェックボックスは、出力れいやの指定の後に来た方がいいかと思いますが、対応できますか？
<img width="678" alt="image" src="https://github.com/user-attachments/assets/9f759511-b8bf-447c-b205-8cff499668b8" />


参考（クリップ）
<img width="704" alt="image" src="https://github.com/user-attachments/assets/397714f5-026d-4290-80e9-444174440f47" />


author_association: CONTRIBUTOR
user: login: nbayashi
created_at: 2025-04-10T07:47:31Z
body: あ、ちょっと待って、一応Windowsでも動作検証する
author_association: CONTRIBUTOR
user: login: nbayashi
created_at: 2025-04-10T08:00:39Z
body: 追加でコメント

この右側の説明のところに各パラメータの説明があるととてもいいと思いました。
<img width="511" alt="image" src="https://github.com/user-attachments/assets/770d4216-a5db-42ef-90ec-6042fd3a8e89" />

現状、各パラメータがどんな意味を持つのかREADMEにも書いてないのでわからない



あと、READMEに記載している「使用方法」で
「高度なオプション及び 処理終了後，自動でウィンドウを閉じるの使用は任意です。」の文章は元々のプラグインでのものなので、削除していいかと。（新しいUIには「高度なオプション」という文字も「処理終了後，自動でウィンドウを閉じる」という文字も表記されていない）
<img width="529" alt="image" src="https://github.com/user-attachments/assets/b2631552-0dee-4d29-b4d4-18a4bad9a26a" />


author_association: CONTRIBUTOR
user: login: Geo-Jagaimo
created_at: 2025-04-10T08:06:56Z
body: @nbayashi 
 
コメントありがとうございます！
パラメータの説明については、作成当時からわかる人しか使わないだろうという想定で敢えて説明は省略しています。
必要であれば別 Issue で対応します。

READMEについても別 Issue で対応します。
author_association: MEMBER