---
# System prepended metadata

title: QGIS python 程式碼編寫(Pyqgis)

---

# QGIS python 程式碼編寫(Pyqgis)

### 前言
python腳本除一般的運行模式外，也可以編寫成套件，不管是串接不同的演算法(如迭代演算法)，或是自己編寫完整演算法，都大大增加了計算的彈性，與未來應用在其他專案的擴充性。但是與python腳本相關的教學甚少，故有此編。

*Python Qt design又是另外一個東西，如果是想要學Qt相關的，就可以先不用繼續看下去啦。

## 先備知識
1. pyqgis基本操作: 可以參考官方的[vector](https://docs.qgis.org/3.34/en/docs/pyqgis_developer_cookbook/vector.html)與[raster](https://docs.qgis.org/3.34/en/docs/pyqgis_developer_cookbook/raster.html)基本介紹
2. 類別、繼承與物件導向(Class, Inheritance and Object Oriented)
    - 套件的編寫基本上是透過繼承QgsProcessingAlgorithm父類來進行操作
    - 故基本的類別與繼承行為需要有基本的認識
    - pyqgis使用到物件導向概念，需要解析每一個物件的方法(method)與屬性(attribute) (但是官方說明文件中沒有清楚交代)
3. 圖形化套件的編寫(graphic modeler)
    - 圖形化套件實現了串接多項不同的演算法，簡化分析工作。
    - 可以先用圖形化套件建立初步算法串接流程，再產出腳本進行編輯。
    - **通常大部分的工作都可以透過graphic modeler進行分析**，只有在進行比較複雜的運算時，如:調用圖徽屬性進行運算(當演算法不支援時)、資料爬蟲(個人認為實用)等，便需要透過python script來進行分析。
    - 
## 基本架構
為了讓編寫的程式碼可以被未來不一樣的專案作利用，編寫成"視窗套件"應該最佳解!
視窗套件以類別為主要架構(通常先使用modeler建立input與基本運算可以省下大筆時間):
```python!
class Model(QgsProcessingAlgorithm): 
    def initAlgorithm(self, config=None):
        ...
    def processAlgorithm(self, parameters, context, model_feedback):
        ...
    def name(self):
        return 'modelName'

    def displayName(self):
        return 'modelDisplayName'

    def group(self):
        return ''

    def groupId(self):
        return ''

    def createInstance(self):
        return Model()
```
- 繼承自QGIS父類別，因此可以做圍套件進行編輯。
- 類別中有幾項必須函式
    - initAlgorithm: 定義套件的輸入及輸出，必須傳入self, config
    - processAlgorithm: 定義算法，必須傳入self,parameters, context, model_feedback
    - name、displayName、group、groupId: 名稱
    - createInstance: 建立實作

1. initAlgorithm
- 透過self.addParameter()來進行傳入變數增加(方法來自父類別)[link](https://qgis.org/pyqgis/3.40/core/QgsProcessingAlgorithm.html#qgis.core.QgsProcessingAlgorithm.addParameter)
- 也有self.addOutput() [link](https://qgis.org/pyqgis/3.40/core/QgsProcessingAlgorithm.html#qgis.core.QgsProcessingAlgorithm.addOutput)
- 內部透過傳入QgsProcessingParameterVectorLayer等來進行變數增加
2. processingAlgorithm

## Debug tools

在編寫過程中，可能會需要利用console來進行debug
然而script modeler 沒辦法單純利用print來進行文字輸出
需要透過pushInfo來進行debug
```python=
class Model(QgsProcessingAlgorithm):
    ...
    
    def processingAlgorithm(self,parameters, context, model_feedback):
        model_feedback.pushInfo('the text we would like to print')
```


## ex01: 迴圈編寫
- python model 如果想要對圖徽進行遍歷(for opeation)一般可以透過iterate over feature 的功能來完成，但卻無法調用圖徽屬性(attribute table)來進行計算。
:::info
舉例而言，如果今天我想要對點向量，進行DEM的viewshed分析，分析完成後依照點的屬性名稱重新命名檔案，在目前的qgis框架下後者是無法達成的。
:::
- 如果情況允許，以modeler來執行較容易編輯，但如果碰到複雜的loop over feature時，Pyqgis的使用便會大幅的增加的套件的使用方便程度。
- 通常我會先使用modeler建立input跟模板再生成維python code來進行編輯。

核心使用函式
1. QgsProcessingAlgorithm.parameterAsVectorLayer() 
2. getFeatures()

在model class中，可以透過(1)來取得"向量圖層"物件，並透過(2)來取得"圖徽"
- 圖徽可以字典取用屬性表格內容(example code#32)
- 如果需要圖徽的幾何資訊，則可以透過geometry()來取得[QgsGeometry物件](https://qgis.org/pyqgis/3.40/core/QgsGeometry.html#qgis.core.QgsGeometry.asPoint)
    - 這邊透過AsPoint回傳[QgsPointXY物件](https://qgis.org/pyqgis/3.40/core/QgsPointXY.html#qgis.core.QgsPointXY)
    - 也可以先用type()檢核圖徽種類(point,line,polygon,unknown,null)



### 範例
```python=
from qgis.core import QgsProcessing
from qgis.core import QgsProcessingAlgorithm
from qgis.core import QgsProcessingMultiStepFeedback
from qgis.core import QgsProcessingParameterVectorLayer,QgsProcessingParameterNumber,QgsProcessingParameterFile,QgsProcessingParameterRasterLayer
from qgis.core import QgsExpression

class Model(QgsProcessingAlgorithm):
    def initAlgorithm(self, config=None):
        self.addParameter(QgsProcessingParameterVectorLayer('pointlayer', 'PointLayer', defaultValue=None))
        self.addParameter(QgsProcessingParameterRasterLayer('dem', 'DEM', defaultValue=None))
        self.addParameter(QgsProcessingParameterNumber('distance', 'distance', type=QgsProcessingParameterNumber.Double, defaultValue=None))
        self.addParameter(QgsProcessingParameterFile('outputFolder', 'outputFolder',behavior=QgsProcessingParameterFile.Folder, fileFilter='All files (*.*)',defaultValue=None))
        
    def processAlgorithm(self, parameters, context, model_feedback):
        # Use a multi-step feedback, so that individual child algorithm progress reports are adjusted for the
        # overall progress through the model
        feedback = QgsProcessingMultiStepFeedback(1, model_feedback)
        results = {}
        outputs = {}
        folder=parameters['outputFolder']
        
        pointVect= self.parameterAsVectorLayer(parameters,'pointlayer',context)
        epsg=pointVect.crs().postgisSrid()
        # Viewshed
        for i,feature in enumerate(pointVect.getFeatures()):
            point=feature.geometry().asPoint()
            model_feedback.pushInfo('{0}, {1} [EPSG:{2}]'.format(point.x(), point.y(),epsg))

            alg_params = {
                'BAND': 1,
                'EXTRA': '',
                'INPUT': parameters['dem'],
                'MAX_DISTANCE': parameters['distance'],
                'OBSERVER': '{0}, {1} [EPSG:{2}]'.format(point.x(), point.y(),epsg),
                'OBSERVER_HEIGHT': 1,
                'OPTIONS': '',
                'OUTPUT': path.join(folder,feature['CELLADDR']+'.tif'),
                'TARGET_HEIGHT': 1,
            }
            outputs['Viewshed'] = processing.run('gdal:viewshed', alg_params, context=context, feedback=feedback, is_child_algorithm=True)
        return results   
    ## the following code is ommited ...
```
