중요

번역은 여러분이 참여할 수 있는 커뮤니티 활동입니다. 이 페이지는 현재 100.00% 번역되었습니다.

27.9. 새 공간 처리 알고리즘을 파이썬 스크립트로 작성하기

파이썬을 사용해서 공간 처리 알고리즘을 작성하기 위한 옵션이 2개 있습니다.

QGIS 내부에서, Processing Toolbox 상단에 있는 Scripts 메뉴에서 Create new script 를 선택하면 사용자 코드를 작성할 수 있는 Processing Script Editor 가 열립니다. 이 작업을 단순화하려면, 동일한 메뉴에서 Create new script from template 을 선택해서 스크립트 템플릿을 바탕으로 사용자 코드를 작성할 수 있습니다. 이 메뉴는 QgsProcessingAlgorithm 을 확장하는 템플릿을 엽니다.

scripts 폴더(기본 저장 위치)에 스크립트를 .py 확장자를 붙여 저장하면, Processing Toolbox 에서 해당 알고리즘을 사용할 수 있습니다.

27.9.1. QgsProcessingAlgorithm 확장

다음 코드는:

  1. 벡터 데이터를 입력물로 받습니다.

  2. 피처의 개수를 셉니다.

  3. 버퍼 작업을 수행합니다.

  4. 버퍼 작업의 결과물로부터 래스터 레이어를 생성합니다.

  5. 버퍼 레이어, 래스터 레이어, 그리고 피처의 개수를 반환합니다.

  1from qgis.PyQt.QtCore import QCoreApplication
  2from qgis.core import (QgsProcessing,
  3                       QgsProcessingAlgorithm,
  4                       QgsProcessingException,
  5                       QgsProcessingOutputNumber,
  6                       QgsProcessingParameterDistance,
  7                       QgsProcessingParameterFeatureSource,
  8                       QgsProcessingParameterVectorDestination,
  9                       QgsProcessingParameterRasterDestination)
 10from qgis import processing
 11
 12
 13class ExampleProcessingAlgorithm(QgsProcessingAlgorithm):
 14    """
 15    This is an example algorithm that takes a vector layer,
 16    creates some new layers and returns some results.
 17    """
 18
 19    def tr(self, string):
 20        """
 21        Returns a translatable string with the self.tr() function.
 22        """
 23        return QCoreApplication.translate('Processing', string)
 24
 25    def createInstance(self):
 26        # Must return a new copy of your algorithm.
 27        return ExampleProcessingAlgorithm()
 28
 29    def name(self):
 30        """
 31        Returns the unique algorithm name.
 32        """
 33        return 'bufferrasterextend'
 34
 35    def displayName(self):
 36        """
 37        Returns the translated algorithm name.
 38        """
 39        return self.tr('Buffer and export to raster (extend)')
 40
 41    def group(self):
 42        """
 43        Returns the name of the group this algorithm belongs to.
 44        """
 45        return self.tr('Example scripts')
 46
 47    def groupId(self):
 48        """
 49        Returns the unique ID of the group this algorithm belongs
 50        to.
 51        """
 52        return 'examplescripts'
 53
 54    def shortHelpString(self):
 55        """
 56        Returns a localised short help string for the algorithm.
 57        """
 58        return self.tr('Example algorithm short description')
 59
 60    def initAlgorithm(self, config=None):
 61        """
 62        Here we define the inputs and outputs of the algorithm.
 63        """
 64        # 'INPUT' is the recommended name for the main input
 65        # parameter.
 66        self.addParameter(
 67            QgsProcessingParameterFeatureSource(
 68                'INPUT',
 69                self.tr('Input vector layer'),
 70                types=[QgsProcessing.TypeVectorAnyGeometry]
 71            )
 72        )
 73        self.addParameter(
 74            QgsProcessingParameterVectorDestination(
 75                'BUFFER_OUTPUT',
 76                self.tr('Buffer output'),
 77            )
 78        )
 79        # 'OUTPUT' is the recommended name for the main output
 80        # parameter.
 81        self.addParameter(
 82            QgsProcessingParameterRasterDestination(
 83                'OUTPUT',
 84                self.tr('Raster output')
 85            )
 86        )
 87        self.addParameter(
 88            QgsProcessingParameterDistance(
 89                'BUFFERDIST',
 90                self.tr('BUFFERDIST'),
 91                defaultValue = 1.0,
 92                # Make distance units match the INPUT layer units:
 93                parentParameterName='INPUT'
 94            )
 95        )
 96        self.addParameter(
 97            QgsProcessingParameterDistance(
 98                'CELLSIZE',
 99                self.tr('CELLSIZE'),
100                defaultValue = 10.0,
101                parentParameterName='INPUT'
102            )
103        )
104        self.addOutput(
105            QgsProcessingOutputNumber(
106                'NUMBEROFFEATURES',
107                self.tr('Number of features processed')
108            )
109        )
110
111    def processAlgorithm(self, parameters, context, feedback):
112        """
113        Here is where the processing itself takes place.
114        """
115        # First, we get the count of features from the INPUT layer.
116        # This layer is defined as a QgsProcessingParameterFeatureSource
117        # parameter, so it is retrieved by calling
118        # self.parameterAsSource.
119        input_featuresource = self.parameterAsSource(parameters,
120                                                     'INPUT',
121                                                     context)
122        numfeatures = input_featuresource.featureCount()
123
124        # Retrieve the buffer distance and raster cell size numeric
125        # values. Since these are numeric values, they are retrieved
126        # using self.parameterAsDouble.
127        bufferdist = self.parameterAsDouble(parameters, 'BUFFERDIST',
128                                            context)
129        rastercellsize = self.parameterAsDouble(parameters, 'CELLSIZE',
130                                                context)
131        if feedback.isCanceled():
132            return {}
133        buffer_result = processing.run(
134            'native:buffer',
135            {
136                # Here we pass on the original parameter values of INPUT
137                # and BUFFER_OUTPUT to the buffer algorithm.
138                'INPUT': parameters['INPUT'],
139                'OUTPUT': parameters['BUFFER_OUTPUT'],
140                'DISTANCE': bufferdist,
141                'SEGMENTS': 10,
142                'DISSOLVE': True,
143                'END_CAP_STYLE': 0,
144                'JOIN_STYLE': 0,
145                'MITER_LIMIT': 10
146            },
147            # Because the buffer algorithm is being run as a step in
148            # another larger algorithm, the is_child_algorithm option
149            # should be set to True
150            is_child_algorithm=True,
151            #
152            # It's important to pass on the context and feedback objects to
153            # child algorithms, so that they can properly give feedback to
154            # users and handle cancelation requests.
155            context=context,
156            feedback=feedback)
157
158        # Check for cancelation
159        if feedback.isCanceled():
160            return {}
161
162        # Run the separate rasterization algorithm using the buffer result
163        # as an input.
164        rasterized_result = processing.run(
165            'qgis:rasterize',
166            {
167                # Here we pass the 'OUTPUT' value from the buffer's result
168                # dictionary off to the rasterize child algorithm.
169                'LAYER': buffer_result['OUTPUT'],
170                'EXTENT': buffer_result['OUTPUT'],
171                'MAP_UNITS_PER_PIXEL': rastercellsize,
172                # Use the original parameter value.
173                'OUTPUT': parameters['OUTPUT']
174            },
175            is_child_algorithm=True,
176            context=context,
177            feedback=feedback)
178
179        if feedback.isCanceled():
180            return {}
181
182        # Return the results
183        return {'OUTPUT': rasterized_result['OUTPUT'],
184                'BUFFER_OUTPUT': buffer_result['OUTPUT'],
185                'NUMBEROFFEATURES': numfeatures}

공간 처리 알고리즘 표준 함수:

  • createInstance (필수)

    사용자 알고리즘의 새 복사본을 반환해야만 합니다. 클래스명을 변경할 경우, 이 함수가 반환하는 값도 일치하도록 업데이트하는 것을 잊지 마세요!

  • name (필수)

    알고리즘을 식별하는 데 사용되는 유일한 알고리즘명을 반환합니다.

  • displayName (필수)

    변형된(translated) 알고리즘명을 반환합니다.

  • group

    이 알고리즘이 종속된 그룹의 이름을 반환합니다.

  • groupId

    이 알고리즘이 종속된 그룹의 유일한 ID를 반환합니다.

  • shortHelpString

    알고리즘을 위한, 번역된(localized) 짧은 도움말 문자열을 반환합니다.

  • initAlgorithm (필수)

    이 함수에서 알고리즘의 입력물과 산출물을 정의합니다.

    주요 입력물 및 주요 산출물 파라미터의 이름으로 각각 INPUTOUTPUT 을 추천합니다.

    파라미터가 다른 파라미터를 의존하는 경우, 해당 관계를 지정하기 위해 parentParameterName 을 사용합니다. (레이어의 필드/밴드 또는 레이어의 거리 단위일 수 있습니다.)

  • processAlgorithm (필수)

    공간 처리를 실행하는 함수입니다.

    parameterAsSourceparameterAsDouble 과 같은 특별한 목적을 위한 함수를 사용해서 파라미터를 받아옵니다.

    공간 처리 알고리즘에서 다른 공간 처리 알고리즘을 실행하는 데 processing.run 을 사용할 수 있습니다. 첫 번째 파라미터는 알고리즘의 이름이고, 두 번째 파라미터는 알고리즘이 받는 파라미터의 목록입니다. 알고리즘 내부에서 또다른 알고리즘을 실행하는 경우 보통 is_child_algorithmTrue 로 설정합니다. contextfeedback 은 알고리즘에 실행 환경 및 사용자와의 (취소 요청을 받거나, 진행 과정을 보고하거나, 텍스트 피드백을 제공하는 등) 통신 채널에 대해 알려줍니다. “자식” 알고리즘이 받는 파라미터로 (부모) 알고리즘의 파라미터를 사용하는 경우, 원본 파라미터 값을 사용해야 합니다. (예: parameters['OUTPUT'])

    취소에 대해 논리적으로 가능한만큼 자주 피드백 객체를 확인하는 편이 좋습니다! 이렇게 하면 사용자가 원하지 않는 공간 처리 과정을 기다리는 대신 즉각 취소할 수 있으니까요.

    알고리즘은 목록(dictionary)으로 정의한 모든 산출물 파라미터에 대해 값을 반환해야 합니다. 이 경우, 해당 값은 공간 처리한 버퍼, 래스터화 산출 레이어 그리고 피처 개수입니다. 목록 키는 원본 파라미터/산출물 이름과 일치해야만 합니다.

27.9.2. @alg 장식자

@alg 장식자(decorator)를 이용하면, 파이썬 코드를 작성한 다음 작성한 코드를 제대로 된 공간 처리 알고리즘으로 만드는 데 필요한 추가 정보를 제공하는 몇 줄을 더 추가해서 사용자 고유의 알고리즘을 생성할 수 있습니다. 이렇게 하면 알고리즘 생성 및 입력물/산출물의 지정을 간단히 완료할 수 있습니다.

장식자 접근법의 중요 제약 가운데 하나는, 이 방법으로 생성된 알고리즘이 언제나 사용자의 공간 처리 스크립트 제공자에 추가될 것이라는 점입니다. 이런 알고리즘을, 예를 들어 플러그인에서 사용하기 위해 사용자 지정 제공자에 추가할 수는 없습니다.

다음 코드는 @alg 장식자를 사용해서:

  1. 벡터 데이터를 입력물로 받습니다.

  2. 피처의 개수를 셉니다.

  3. 버퍼 작업을 수행합니다.

  4. 버퍼 작업의 결과물로부터 래스터 레이어를 생성합니다.

  5. 버퍼 레이어, 래스터 레이어, 그리고 피처의 개수를 반환합니다.

 1from qgis import processing
 2from qgis.processing import alg
 3from qgis.core import QgsProject
 4
 5@alg(name='bufferrasteralg', label='Buffer and export to raster (alg)',
 6     group='examplescripts', group_label='Example scripts')
 7# 'INPUT' is the recommended name for the main input parameter
 8@alg.input(type=alg.SOURCE, name='INPUT', label='Input vector layer')
 9# 'OUTPUT' is the recommended name for the main output parameter
10@alg.input(type=alg.RASTER_LAYER_DEST, name='OUTPUT',
11           label='Raster output')
12@alg.input(type=alg.VECTOR_LAYER_DEST, name='BUFFER_OUTPUT',
13           label='Buffer output')
14@alg.input(type=alg.DISTANCE, name='BUFFERDIST', label='BUFFER DISTANCE',
15           default=1.0)
16@alg.input(type=alg.DISTANCE, name='CELLSIZE', label='RASTER CELL SIZE',
17           default=10.0)
18@alg.output(type=alg.NUMBER, name='NUMBEROFFEATURES',
19            label='Number of features processed')
20
21def bufferrasteralg(instance, parameters, context, feedback, inputs):
22    """
23    Description of the algorithm.
24    (If there is no comment here, you will get an error)
25    """
26    input_featuresource = instance.parameterAsSource(parameters,
27                                                     'INPUT', context)
28    numfeatures = input_featuresource.featureCount()
29    bufferdist = instance.parameterAsDouble(parameters, 'BUFFERDIST',
30                                            context)
31    rastercellsize = instance.parameterAsDouble(parameters, 'CELLSIZE',
32                                                context)
33    if feedback.isCanceled():
34        return {}
35    buffer_result = processing.run('native:buffer',
36                               {'INPUT': parameters['INPUT'],
37                                'OUTPUT': parameters['BUFFER_OUTPUT'],
38                                'DISTANCE': bufferdist,
39                                'SEGMENTS': 10,
40                                'DISSOLVE': True,
41                                'END_CAP_STYLE': 0,
42                                'JOIN_STYLE': 0,
43                                'MITER_LIMIT': 10
44                                },
45                               is_child_algorithm=True,
46                               context=context,
47                               feedback=feedback)
48    if feedback.isCanceled():
49        return {}
50    rasterized_result = processing.run('qgis:rasterize',
51                               {'LAYER': buffer_result['OUTPUT'],
52                                'EXTENT': buffer_result['OUTPUT'],
53                                'MAP_UNITS_PER_PIXEL': rastercellsize,
54                                'OUTPUT': parameters['OUTPUT']
55                               },
56                               is_child_algorithm=True, context=context,
57                               feedback=feedback)
58    if feedback.isCanceled():
59        return {}
60    return {'OUTPUT': rasterized_result['OUTPUT'],
61            'BUFFER_OUTPUT': buffer_result['OUTPUT'],
62            'NUMBEROFFEATURES': numfeatures}

코드를 보면 알 수 있듯이 2개의 알고리즘(‘native:buffer’ 및 ‘qgis:rasterize’)을 사용합니다. 후자(‘qgis:rasterize’)는 전자(‘native:buffer’)가 생성한 버퍼 레이어로부터 래스터 레이어를 생성합니다.

이전 장을 읽어보았다면 이 공간 처리 과정이 일어나는 코드 부분을 이해하기 어렵지는 않을 겁니다. 하지만 처음 몇 줄의 경우 추가 설명이 필요합니다. 이 줄들은 사용자의 코드를 툴박스 또는 모델 설계자 같은 어떤 GUI 구성 요소에서도 실행할 수 있는 알고리즘으로 변환시키는 데 필요한 정보를 제공하고 있습니다.

이 줄들은 모두 알고리즘 코딩 작업을 단순화시켜주는 @alg 장식자 함수를 호출하고 있습니다.

  • @alg 장식자를 사용해서 툴박스에 있는 알고리즘의 이름 및 위치를 정의합니다.

  • @alg.input 장식자를 사용해서 알고리즘의 입력물을 정의합니다.

  • @alg.output 장식자를 사용해서 알고리즘의 산출물을 정의합니다.

기존 파라미터와 그에 대응하는 문자열을 알고 싶다면 공간 처리 알고리즘의 입력물 및 산출물 유형 을 읽어보십시오.

27.9.3. 알고리즘 산출물 건네주기

레이어(래스터 또는 벡터)를 표현하는 산출물을 선언하는 경우, 알고리즘 실행 종료시 알고리즘이 QGIS에 산출물을 추가하려 할 것입니다.

  • 래스터 레이어 산출물: QgsProcessingParameterRasterDestination / alg.RASTER_LAYER_DEST

  • 벡터 레이어 산출물: QgsProcessingParameterVectorDestination / alg.VECTOR_LAYER_DEST

따라서 processing.run() 메소드가 사용자의 현재 프로젝트에 자신이 생성한 레이어를 추가하지 않았는데도 불구하고, 산출물 레이어 2개(버퍼 및 래스터 버퍼)를 불러올 것입니다. 두 레이어가 사용자가 입력한 대상 위치에 (또는 사용자가 대상 위치를 지정하지 않은 경우 임시 위치에) 저장되기 때문입니다.

레이어가 알고리즘의 산출물로 생성되는 경우, 산출물로 선언되어야 합니다. 그렇지 않으면 모델 설계자에서 알고리즘을 제대로 사용할 수 없습니다. 선언한 내용과 알고리즘이 실제로 생성한 산출물이 일치하지 않을 것이기 때문입니다.

산출물 목록에 문자열, 숫자 등을 지정해서 (“NUMBEROFFEATURES” 예시에서 보였듯이) 문자열, 숫자 등을 반환시킬 수 있습니다. 그러나 항상 사용자 알고리즘에서 나온 산출물로 명확하게 정의해야 합니다. 알고리즘이 유용한 값을 가능한 한 많이 산출하는 것을 권장합니다. 사용자 알고리즘이 모델의 일부로 사용되는 경우 다음 알고리즘에 매우 유용하게 사용할 수 있기 때문입니다.

27.9.4. 사용자에게 정보 전달하기

사용자 알고리즘의 처리 시간이 오래 걸릴 경우, 사용자에게 진행 상황을 알려주는 편이 좋습니다. feedback (QgsProcessingFeedback)을 사용하면 됩니다.

setProgressText(text)setProgress(percent) 메소드 2개를 사용하면 각각 진행 상황 텍스트 및 진행 상황 막대를 업데이트할 수 있습니다.

pushCommandInfo(text), pushDebugInfo(text), pushInfo(text) 그리고 reportError(text) 메소드를 사용하면 더 많은 정보를 제공할 수 있습니다.

사용자 스크립트에 문제가 있을 경우, 해당 문제를 처리하는 올바른 방법은 QgsProcessingException 예외를 발생시키는 것입니다. 예외 작성자에게 메시지를 인자로 넘겨줄 수 있습니다. 공간 처리 프레임워크는 알고리즘이 어디서 (툴박스, 모델 설계자, 파이썬 콘솔 등등) 실행되느냐에 따라 예외를 처리하고 사용자에게 알려줄 것입니다.

27.9.5. 사용자 스크립트 문서 작성하기

QgsProcessingAlgorithm 클래스의 helpString()helpUrl() 메소드를 과부하(overload)시키면 사용자 스크립트를 문서화할 수 있습니다.

27.9.6. 플래그

QgsProcessingAlgorithm 클래스의 flags() 메소드를 무시하면 QGIS에 사용자 알고리즘에 대해 더 많이 알려줄 수 있습니다. 예를 들어 QGIS에 스크립트를 모델 설계자로부터 숨겨야 한다고, 취소될 수 있다고, 스레드 편에서 안전하지 않다고, 그 외의 많은 것을 알려줄 수 있습니다.

기본적으로 공간 처리 프레임워크는 공간 처리 작업을 실행하는 동안 QGIS가 즉시 반응할 수 있도록 유지하기 위해 알고리즘을 개별 스레드로 실행합니다. 사용자 알고리즘이 정기적으로 충돌을 일으킨다면, 배경 스레드에서 작업하기엔 안전하지 않은 API 호출을 사용하고 있을 가능성이 높습니다. 공간 처리 프레임워크가 그 대신 사용자 알고리즘을 주요 스레드에서 실행시키도록 강제하려면, 사용자 알고리즘의 flags() 메소드에서 QgsProcessingAlgorithm.FlagNoThreading 플래그를 반환하게 해보십시오.

27.9.7. 스크립트 알고리즘 작성을 위한 모범적인 예제

사용자가 자신의 스크립트 알고리즘을 생성하는 경우, 그 중에서도 특히 생성한 스크립트를 다른 QGIS 사용자와 공유하고 싶은 경우 고려해야 할 사항들을 정리해봤습니다. 이 간단한 규칙을 따른다면 툴박스, 모델 설계자, 배치 프로세스 인터페이스와 같은 서로 다른 공간 처리 구성 요소를 아우르는 일관성을 유지할 수 있을 겁니다.

  • 산출 레이어를 불러오지 마십시오. 공간 처리 프레임워크가 사용자의 산출물을 처리하게 하고, 필요한 경우 불러오십시오.

  • 항상 사용자 알고리즘이 생성한 산출물을 선언하십시오.

  • 스크립트에서 메시지 상자를 표시한다는가 하는, 어떤 GUI 항목도 사용하지 마십시오. 사용자에게 정보를 제공하고 싶은 경우, 피드백 객체(QgsProcessingFeedback) 메소드를 사용하거나 또는 QgsProcessingException 예외를 날리십시오.

QGIS는 이미 수많은 공간 처리 알고리즘을 제공하고 있습니다. QGIS 저장소에서 코드를 찾아볼 수 있습니다.