22.8. 새 공간 처리 알고리즘을 파이썬 스크립트로 작성¶
파이썬을 사용해서 공간 처리 알고리즘을 작성하기 위한 옵션이 2개 있습니다.
QGIS 내부에서, Processing Toolbox 상단에 있는 Scripts 메뉴에서 Create new script 를 선택하면 사용자 코드를 작성할 수 있는 Processing Script Editor 가 열립니다. 이 작업을 단순화하려면, 동일한 메뉴에서 Create new script from template 을 선택해서 스크립트 템플릿을 바탕으로 사용자 코드를 작성할 수 있습니다. 이 메뉴는 QgsProcessingAlgorithm
을 확장하는 템플릿을 엽니다.
scripts
폴더(기본 저장 위치)에 스크립트를 .py
확장자를 붙여 저장하면, Processing Toolbox 에서 해당 알고리즘을 사용할 수 있습니다.
22.8.1. QgsProcessingAlgorithm 확장¶
다음 코드는:
벡터 데이터를 입력물로 받습니다.
피처의 개수를 셉니다.
버퍼 작업을 수행합니다.
버퍼 작업의 결과물로부터 래스터 레이어를 생성합니다.
버퍼 레이어, 래스터 레이어, 그리고 피처의 개수를 반환합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 | from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingException,
QgsProcessingOutputNumber,
QgsProcessingParameterDistance,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterVectorDestination,
QgsProcessingParameterRasterDestination)
from qgis import processing
class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
"""
This is an example algorithm that takes a vector layer,
creates some new layers and returns some results.
"""
def tr(self, string):
"""
Returns a translatable string with the self.tr() function.
"""
return QCoreApplication.translate('Processing', string)
def createInstance(self):
# Must return a new copy of your algorithm.
return ExampleProcessingAlgorithm()
def name(self):
"""
Returns the unique algorithm name.
"""
return 'bufferrasterextend'
def displayName(self):
"""
Returns the translated algorithm name.
"""
return self.tr('Buffer and export to raster (extend)')
def group(self):
"""
Returns the name of the group this algorithm belongs to.
"""
return self.tr('Example scripts')
def groupId(self):
"""
Returns the unique ID of the group this algorithm belongs
to.
"""
return 'examplescripts'
def shortHelpString(self):
"""
Returns a localised short help string for the algorithm.
"""
return self.tr('Example algorithm short description')
def initAlgorithm(self, config=None):
"""
Here we define the inputs and outputs of the algorithm.
"""
# 'INPUT' is the recommended name for the main input
# parameter.
self.addParameter(
QgsProcessingParameterFeatureSource(
'INPUT',
self.tr('Input vector layer'),
types=[QgsProcessing.TypeVectorAnyGeometry]
)
)
self.addParameter(
QgsProcessingParameterVectorDestination(
'BUFFER_OUTPUT',
self.tr('Buffer output'),
)
)
# 'OUTPUT' is the recommended name for the main output
# parameter.
self.addParameter(
QgsProcessingParameterRasterDestination(
'OUTPUT',
self.tr('Raster output')
)
)
self.addParameter(
QgsProcessingParameterDistance(
'BUFFERDIST',
self.tr('BUFFERDIST'),
defaultValue = 1.0,
# Make distance units match the INPUT layer units:
parentParameterName='INPUT'
)
)
self.addParameter(
QgsProcessingParameterDistance(
'CELLSIZE',
self.tr('CELLSIZE'),
defaultValue = 10.0,
parentParameterName='INPUT'
)
)
self.addOutput(
QgsProcessingOutputNumber(
'NUMBEROFFEATURES',
self.tr('Number of features processed')
)
)
def processAlgorithm(self, parameters, context, feedback):
"""
Here is where the processing itself takes place.
"""
# First, we get the count of features from the INPUT layer.
# This layer is defined as a QgsProcessingParameterFeatureSource
# parameter, so it is retrieved by calling
# self.parameterAsSource.
input_featuresource = self.parameterAsSource(parameters,
'INPUT',
context)
numfeatures = input_featuresource.featureCount()
# Retrieve the buffer distance and raster cell size numeric
# values. Since these are numeric values, they are retrieved
# using self.parameterAsDouble.
bufferdist = self.parameterAsDouble(parameters, 'BUFFERDIST',
context)
rastercellsize = self.parameterAsDouble(parameters, 'CELLSIZE',
context)
if feedback.isCanceled():
return {}
buffer_result = processing.run(
'native:buffer',
{
# Here we pass on the original parameter values of INPUT
# and BUFFER_OUTPUT to the buffer algorithm.
'INPUT': parameters['INPUT'],
'OUTPUT': parameters['BUFFER_OUTPUT'],
'DISTANCE': bufferdist,
'SEGMENTS': 10,
'DISSOLVE': True,
'END_CAP_STYLE': 0,
'JOIN_STYLE': 0,
'MITER_LIMIT': 10
},
# Because the buffer algorithm is being run as a step in
# another larger algorithm, the is_child_algorithm option
# should be set to True
is_child_algorithm=True,
#
# It's important to pass on the context and feedback objects to
# child algorithms, so that they can properly give feedback to
# users and handle cancelation requests.
context=context,
feedback=feedback)
# Check for cancelation
if feedback.isCanceled():
return {}
# Run the separate rasterization algorithm using the buffer result
# as an input.
rasterized_result = processing.run(
'qgis:rasterize',
{
# Here we pass the 'OUTPUT' value from the buffer's result
# dictionary off to the rasterize child algorithm.
'LAYER': buffer_result['OUTPUT'],
'EXTENT': buffer_result['OUTPUT'],
'MAP_UNITS_PER_PIXEL': rastercellsize,
# Use the original parameter value.
'OUTPUT': parameters['OUTPUT']
},
is_child_algorithm=True,
context=context,
feedback=feedback)
if feedback.isCanceled():
return {}
# Return the results
return {'OUTPUT': rasterized_result['OUTPUT'],
'BUFFER_OUTPUT': buffer_result['OUTPUT'],
'NUMBEROFFEATURES': numfeatures}
|
공간 처리 알고리즘 표준 함수:
- createInstance (필수)
사용자 알고리즘의 새 복사본을 반환해야만 합니다. 클래스명을 변경할 경우, 이 함수가 반환하는 값도 일치하도록 업데이트하는 것을 잊지 마세요!
- name (필수)
알고리즘을 식별하는 데 사용되는 유일한 알고리즘명을 반환합니다.
- displayName (필수)
변형된(translated) 알고리즘명을 반환합니다.
- group
이 알고리즘이 종속된 그룹의 명칭을 반환합니다.
- groupId
이 알고리즘이 종속된 그룹의 유일한 ID를 반환합니다.
- shortHelpString
알고리즘을 위한 번역된(localized) 짧은 도움말 문자열을 반환합니다.
- initAlgorithm (필수)
이 함수에서 알고리즘의 입력물과 산출물을 정의합니다.
주요 입력물 및 주요 산출물 파라미터의 명칭으로 각각
INPUT
및OUTPUT
을 추천합니다.파라미터가 다른 파라미터를 의존하는 경우, 해당 관계를 지정하기 위해
parentParameterName
을 사용합니다. (레이어의 필드/밴드 또는 레이어의 거리 단위일 수 있습니다.)
- processAlgorithm (필수)
공간 처리를 실행하는 함수입니다.
parameterAsSource
및parameterAsDouble
과 같은 특별한 목적을 위한 함수를 사용해서 파라미터를 받아옵니다.공간 처리 알고리즘에서 다른 공간 처리 알고리즘을 실행하는 데
processing.run
을 사용할 수 있습니다. 첫 번째 파라미터는 알고리즘의 명칭이고, 두 번째 파라미터는 알고리즘이 받는 파라미터의 목록입니다. 알고리즘 내부에서 또다른 알고리즘을 실행하는 경우 보통is_child_algorithm
을True
로 설정합니다.context
및feedback
은 알고리즘에 실행 환경 및 사용자와의 (취소 요청을 받거나, 진행 과정을 보고하거나, 텍스트 피드백을 제공하는 등) 통신 채널에 대해 알려줍니다. 《자식》 알고리즘이 받는 파라미터로 (부모) 알고리즘의 파라미터를 사용하는 경우, 원본 파라미터 값을 사용해야 합니다. (예:parameters['OUTPUT']
)취소에 대해 논리적으로 가능한만큼 자주 피드백 객체를 확인하는 편이 좋습니다! 이렇게 하면 사용자가 원하지 않는 공간 처리 과정을 기다리는 대신 즉각 취소할 수 있으니까요.
알고리즘은 목록(dictionary)으로 정의한 모든 산출물 파라미터에 대해 값을 반환해야 합니다. 이 경우, 해당 값은 공간 처리한 버퍼, 래스터화 산출 레이어 그리고 피처 개수입니다. 목록 키는 원본 파라미터/산출물 명칭과 일치해야만 합니다.
22.8.2. @alg 장식자¶
@alg 장식자(decorator)를 이용하면, 파이썬 코드를 작성한 다음 작성한 코드를 제대로 된 공간 처리 알고리즘으로 만드는 데 필요한 추가 정보를 제공하는 몇 줄을 더 추가해서 사용자 고유의 알고리즘을 생성할 수 있습니다. 이렇게 하면 알고리즘 생성 및 입력물/산출물의 지정을 간단히 완료할 수 있습니다.
장식자 접근법의 중요 제약 가운데 하나는, 이 방법으로 생성된 알고리즘이 언제나 사용자의 공간 처리 스크립트 제공자에 추가될 것이라는 점입니다. 이런 알고리즘을, 예를 들어 플러그인에서 사용하기 위해 사용자 지정 제공자에 추가할 수는 없습니다.
다음 코드는 @alg 장식자를 사용해서:
벡터 데이터를 입력물로 받습니다.
피처의 개수를 셉니다.
버퍼 작업을 수행합니다.
버퍼 작업의 결과물로부터 래스터 레이어를 생성합니다.
버퍼 레이어, 래스터 레이어, 그리고 피처의 개수를 반환합니다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 | from qgis import processing
from qgis.processing import alg
from qgis.core import QgsProject
@alg(name='bufferrasteralg', label='Buffer and export to raster (alg)',
group='examplescripts', group_label='Example scripts')
# 'INPUT' is the recommended name for the main input parameter
@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector layer')
# 'OUTPUT' is the recommended name for the main output parameter
@alg.input(type=alg.RASTER_LAYER_DEST, name='OUTPUT',
label='Raster output')
@alg.input(type=alg.VECTOR_LAYER_DEST, name='BUFFER_OUTPUT',
label='Buffer output')
@alg.input(type=alg.DISTANCE, name='BUFFERDIST', label='BUFFER DISTANCE',
default=1.0)
@alg.input(type=alg.DISTANCE, name='CELLSIZE', label='RASTER CELL SIZE',
default=10.0)
@alg.output(type=alg.NUMBER, name='NUMBEROFFEATURES',
label='Number of features processed')
def bufferrasteralg(instance, parameters, context, feedback, inputs):
"""
Description of the algorithm.
(If there is no comment here, you will get an error)
"""
input_featuresource = instance.parameterAsSource(parameters,
'INPUT', context)
numfeatures = input_featuresource.featureCount()
bufferdist = instance.parameterAsDouble(parameters, 'BUFFERDIST',
context)
rastercellsize = instance.parameterAsDouble(parameters, 'CELLSIZE',
context)
if feedback.isCanceled():
return {}
buffer_result = processing.run('native:buffer',
{'INPUT': parameters['INPUT'],
'OUTPUT': parameters['BUFFER_OUTPUT'],
'DISTANCE': bufferdist,
'SEGMENTS': 10,
'DISSOLVE': True,
'END_CAP_STYLE': 0,
'JOIN_STYLE': 0,
'MITER_LIMIT': 10
},
is_child_algorithm=True,
context=context,
feedback=feedback)
if feedback.isCanceled():
return {}
rasterized_result = processing.run('qgis:rasterize',
{'LAYER': buffer_result['OUTPUT'],
'EXTENT': buffer_result['OUTPUT'],
'MAP_UNITS_PER_PIXEL': rastercellsize,
'OUTPUT': parameters['OUTPUT']
},
is_child_algorithm=True, context=context,
feedback=feedback)
if feedback.isCanceled():
return {}
return {'OUTPUT': rasterized_result['OUTPUT'],
'BUFFER_OUTPUT': buffer_result['OUTPUT'],
'NUMBEROFFEATURES': numfeatures}
|
코드를 보면 알 수 있듯이 2개의 알고리즘(〈native:buffer〉 및 〈qgis:rasterize〉)을 사용합니다. 후자(〈qgis:rasterize〉)는 전자(〈native:buffer〉)가 생성한 버퍼 레이어로부터 래스터 레이어를 생성합니다.
이전 장을 읽어보았다면 이 공간 처리 과정이 일어나는 코드 부분을 이해하기 어렵지는 않을 겁니다. 하지만 처음 몇 줄의 경우 추가 설명이 필요합니다. 이 줄들은 사용자의 코드를 툴박스 또는 그래픽 모델 생성기 같은 어떤 GUI 구성 요소에서도 실행할 수 있는 알고리즘으로 변환시키는 데 필요한 정보를 제공하고 있습니다.
이 줄들은 모두 알고리즘 코딩 작업을 단순화시켜주는 @alg
장식자 함수를 호출하고 있습니다.
@alg 장식자를 사용해서 툴박스에 있는 알고리즘의 명칭 및 위치를 정의합니다.
@alg.input 장식자를 사용해서 알고리즘의 입력물을 정의합니다.
@alg.output 장식자를 사용해서 알고리즘의 산출물을 정의합니다.
22.8.3. 공간 처리 알고리즘의 입력물 및 산출물 유형¶
다음은 공간 처리 프레임워크가 지원하는 입력물 및 산출물 유형을 대응하는 alg 장식자 상수와 함께 정리한 목록입니다. (algfactory.py
파일이 alg 상수의 전체 목록을 담고 있습니다.) 클래스명으로 정렬되어 있습니다.
22.8.3.1. 입력물 유형¶
클래스 |
alg 상수 |
설명 |
---|---|---|
|
사용자가 사용할 수 있는 인증 환경 설정에서 선택하거나 또는 새 인증 환경 설정을 생성할 수 있게 해줍니다. |
|
|
래스터 레이어의 밴드 |
|
|
불(boolean) 값 |
|
|
색상 |
|
|
좌표계 |
|
|
거리 값을 위한 실수형(double) 숫자 파라미터 |
|
|
사전 정의된 값들의 집합에서 선택할 수 있는 집합(enumeration) |
|
|
표현식 |
|
|
xmin, xmax, ymin, ymax로 정의된 공간 범위 |
|
|
벡터 레이어의 속성 테이블에 있는 필드 |
|
|
기존 파일의 파일명 |
|
|
새로 생성된 산출물 파일의 파일명 |
|
|
폴더 |
|
|
정수 |
|
|
조판 |
|
|
조판 항목 |
|
|
맵 레이어 |
|
|
매트릭스 |
|
|
메시 레이어 |
|
|
레이어 집합 |
|
|
숫자 값 |
|
|
포인트 |
|
|
숫자의 범위 |
|
|
래스터 레이어 |
|
|
래스터 레이어 |
|
|
맵 축척 |
|
|
피처 싱크(sink) |
|
|
피처 소스 |
|
맵 축척 |
||
|
텍스트 문자열 |
|
|
벡터 레이어 |
|
|
벡터 레이어 |
22.8.3.2. 산출물 유형¶
클래스 |
alg 상수 |
설명 |
---|---|---|
|
불(boolean) 값 |
|
|
거리 값을 위한 실수형(double) 숫자 파라미터 |
|
|
기존 파일의 파일명 |
|
|
폴더 |
|
|
HTML |
|
|
정수 |
|
|
레이어 정의 |
|
|
맵 레이어 |
|
|
레이어 집합 |
|
|
숫자 값 |
|
|
래스터 레이어 |
|
|
텍스트 문자열 |
|
|
벡터 레이어 |
22.8.4. 알고리즘 산출물 건네주기¶
레이어(래스터 또는 벡터)를 표현하는 산출물을 선언하는 경우, 알고리즘 실행 종료시 알고리즘이 QGIS에 산출물을 추가하려 할 것입니다.
래스터 레이어 산출물: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST
벡터 레이어 산출물: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST
따라서 processing.run()
메소드가 사용자의 현재 프로젝트에 자신이 생성한 레이어를 추가하지 않았는데도 불구하고, 산출물 레이어 2개(버퍼 및 래스터 버퍼)를 불러올 것입니다. 두 레이어가 사용자가 입력한 대상 위치에 (또는 사용자가 대상 위치를 지정하지 않은 경우 임시 위치에) 저장되기 때문입니다.
레이어가 알고리즘의 산출물로 생성되는 경우, 산출물로 선언되어야 합니다. 그렇지 않으면 모델 생성기에서 알고리즘을 제대로 사용할 수 없습니다. 선언한 내용과 알고리즘이 실제로 생성한 산출물이 일치하지 않을 것이기 때문입니다.
산출물 목록에 문자열, 숫자 등을 지정해서 (《NUMBEROFFEATURES》 예시에서 보였듯이) 문자열, 숫자 등을 반환시킬 수 있습니다. 그러나 항상 사용자 알고리즘에서 나온 산출물로 명확하게 정의해야 합니다. 알고리즘이 유용한 값을 가능한 한 많이 산출하는 것을 권장합니다. 사용자 알고리즘이 모델의 일부로 사용되는 경우 다음 알고리즘에 매우 유용하게 사용할 수 있기 때문입니다.
22.8.5. 사용자에게 정보 제공하기¶
사용자 알고리즘의 처리 시간이 오래 걸릴 경우, 사용자에게 진행 상황을 알려주는 편이 좋습니다. feedback
(QgsProcessingFeedback
)을 사용하면 됩니다.
setProgressText(text)
및 setProgress(percent)
메소드 2개를 사용하면 각각 진행 상황 텍스트 및 진행 상황 막대를 업데이트할 수 있습니다.
pushCommandInfo(text)
, pushDebugInfo(text)
, pushInfo(text)
그리고 reportError(text)
메소드를 사용하면 더 많은 정보를 제공할 수 있습니다.
사용자 스크립트에 문제가 있을 경우, 해당 문제를 처리하는 올바른 방법은 QgsProcessingException
예외를 발생시키는 것입니다. 예외 작성자에게 메시지를 인자로 넘겨줄 수 있습니다. 공간 처리 프레임워크는 알고리즘이 어디서 (툴박스, 모델 생성기, 파이썬 콘솔 등등) 실행되느냐에 따라 예외를 처리하고 사용자에게 알려줄 것입니다.
22.8.6. 사용자 스크립트 문서 작성하기¶
QgsProcessingAlgorithm
클래스의 helpString()
및 helpUrl()
메소드를 과부하(overload)시키면 사용자 스크립트를 문서화할 수 있습니다.
22.8.7. 플래그¶
QgsProcessingAlgorithm
클래스의 flags
메소드를 무시하면 QGIS에 사용자 알고리즘에 대해 더 많이 알려줄 수 있습니다. 예를 들어 QGIS에 스크립트를 모델 생성기로부터 숨겨야 한다고, 취소될 수 있다고, 스레드 편에서 안전하지 않다고, 그 외의 많은 것을 알려줄 수 있습니다.
팁
기본적으로 공간 처리 프레임워크는 공간 처리 작업을 실행하는 동안 QGIS가 즉시 반응할 수 있도록 유지하기 위해 알고리즘을 개별 스레드로 실행합니다. 사용자 알고리즘이 정기적으로 충돌을 일으킨다면, 배경 스레드에서 작업하기엔 안전하지 않은 API 호출을 사용하고 있을 가능성이 높습니다. 공간 처리 프레임워크가 그 대신 사용자 알고리즘을 주요 스레드에서 실행시키도록 강제하려면, 사용자 알고리즘의 flags()
메소드에서 QgsProcessingAlgorithm.FlagNoThreading
플래그를 반환하게 해보십시오.
22.8.8. 스크립트 알고리즘 작성을 위한 모범적인 예제¶
사용자가 자신의 스크립트 알고리즘을 생성하는 경우, 그 중에서도 특히 생성한 스크립트를 다른 QGIS 사용자와 공유하고 싶은 경우 고려해야 할 사항들을 정리해봤습니다. 이 간단한 규칙을 따른다면 툴박스, 모델 생성기, 배치 프로세스 인터페이스와 같은 서로 다른 공간 처리 구성 요소를 아우르는 일관성을 유지할 수 있을 겁니다.
산출 레이어를 불러오지 마십시오. 공간 처리 프레임워크가 사용자의 산출물을 처리하게 하고, 필요한 경우 불러오십시오.
항상 사용자 알고리즘이 생성한 산출물을 선언하십시오.
스크립트에서 메시지 상자를 표시한다는가 하는, 어떤 GUI 항목도 사용하지 마십시오. 사용자에게 정보를 제공하고 싶은 경우, 피드백 객체(
QgsProcessingFeedback
) 메소드를 사용하거나 또는QgsProcessingException
예외를 날리십시오.
QGIS는 이미 수많은 공간 처리 알고리즘을 제공하고 있습니다. https://github.com/qgis/QGIS/blob/release-3_10/python/plugins/processing/algs/qgis 에서 코드를 찾아볼 수 있습니다.