중요

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

7. 도형 다루기

힌트

PyQGIS 콘솔을 사용하지 않는 경우 이 페이지에 있는 코드 조각들을 다음과 같이 가져와야 합니다:

 1from qgis.core import (
 2  QgsGeometry,
 3  QgsGeometryCollection,
 4  QgsPoint,
 5  QgsPointXY,
 6  QgsWkbTypes,
 7  QgsProject,
 8  QgsFeatureRequest,
 9  QgsVectorLayer,
10  QgsDistanceArea,
11  QgsUnitTypes,
12  QgsCoordinateTransform,
13  QgsCoordinateReferenceSystem
14)

공간 피처를 표현하는 포인트, 라인스트링, 그리고 폴리곤을 흔히 도형(geometry)이라고 부릅니다. QGIS에서는 도형을 QgsGeometry 클래스를 사용해서 표현합니다.

도형 한 개가 실제로는 단순(단일 부분, single-part) 도형의 집합인 경우가 종종 있습니다. 이런 도형을 다중 부분(multi-part) 도형이라고 합니다. 다중 부분 도형이 한 가지 유형의 단순 도형으로만 이루어져 있을 경우 멀티포인트, 멀티라인스트링, 또는 멀티폴리곤이라고 부릅니다. 예를 들어 여러 개의 섬으로 이루어진 국가라면 멀티폴리곤으로 표현할 수 있습니다.

도형의 좌표는 어떤 좌표계라도 될 수 있습니다. 레이어에서 피처를 불러올 때, 해당 도형은 레이어의 좌표계를 따르는 좌표를 가지게 될 것입니다.

가능한 모든 도형 구조 및 관계에 대한 고급 상세 정보를 알고 싶다면 OGC 단순 피처 접근 표준(OGC Simple Feature Access Standards) 에서 그 설명과 사양을 찾아볼 수 있습니다.

7.1. 도형 작성하기

PyQGIS는 도형을 생성하기 위한 옵션을 몇 개 제공합니다:

  • 좌표로부터

    1gPnt = QgsGeometry.fromPointXY(QgsPointXY(1,1))
    2print(gPnt)
    3gLine = QgsGeometry.fromPolyline([QgsPoint(1, 1), QgsPoint(2, 2)])
    4print(gLine)
    5gPolygon = QgsGeometry.fromPolygonXY([[QgsPointXY(1, 1),
    6    QgsPointXY(2, 2), QgsPointXY(2, 1)]])
    7print(gPolygon)
    

    QgsPoint 클래스 또는 QgsPointXY 클래스를 사용해서 좌표를 지정합니다. 이 두 클래스의 차이라고 하면 QgsPoint 클래스는 M과 Z 차원을 지원한다는 점입니다.

    폴리라인(라인스트링)은 포인트 목록으로 표현됩니다.

    폴리곤은 선형 고리 (예: 닫힌 라인스트링들) 목록으로 표현됩니다. 첫 번째 고리(ring)가 외곽 고리(경계)이며, 있을 수도 있고 없을 수도 있는 그 다음 고리들은 폴리곤에 있는 구멍(hole)입니다. 몇몇 다른 프로그램과는 달리 QGIS는 여러분을 위해 고리를 닫아줄 것이기 때문에 첫 번째 포인트를 마지막 포인트로 복제할 필요가 없다는 사실을 기억하세요.

    다중 부분 도형은 한 단계 심화됩니다. 멀티포인트는 포인트의 목록, 멀티라인스트링은 라인스트링의 목록, 멀티폴리곤은 폴리곤의 목록입니다.

  • WKT(well-known text)로부터

    geom = QgsGeometry.fromWkt("POINT(3 4)")
    print(geom)
    
  • WKB(well-known binary)로부터

    1g = QgsGeometry()
    2wkb = bytes.fromhex("010100000000000000000045400000000000001440")
    3g.fromWkb(wkb)
    4
    5# print WKT representation of the geometry
    6print(g.asWkt())
    

7.2. 도형에 접근하기

먼저 도형 유형을 알아야 합니다. wkbType() 메소드를 사용하면 됩니다. 이 메소드는 QgsWkbTypes.Type 클래스의 열거 목록(enumeration)으로부터 값을 반환합니다.

1print(gPnt.wkbType())
2# output: 'WkbType.Point'
3print(gLine.wkbType())
4# output: 'WkbType.LineString'
5print(gPolygon.wkbType())
6# output: 'WkbType.Polygon'

아니면, QgsWkbTypes.GeometryType 메소드의 열거 목록으로부터 값을 반환하는 type() 메소드를 사용해도 됩니다.

print(gLine.type())
# output: 'GeometryType.Line'

사람이 읽을 수 있는 도형 유형을 얻으려면 displayString() 함수를 사용하십시오.

1print(QgsWkbTypes.displayString(gPnt.wkbType()))
2# output: 'Point'
3print(QgsWkbTypes.displayString(gLine.wkbType()))
4# output: 'LineString'
5print(QgsWkbTypes.displayString(gPolygon.wkbType()))
6# output: 'Polygon'

도형이 다중 부분인지 아닌지 여부를 알려주는 isMultipart() 도우미 함수도 있습니다.

모든 벡터 유형 별로 도형에서 정보를 추출하기 위한 접근자(accessor) 함수들이 있습니다. 다음은 이런 접근자를 어떻게 사용하는지 보여주는 예시입니다:

1print(gPnt.asPoint())
2# output: <QgsPointXY: POINT(1 1)>
3print(gLine.asPolyline())
4# output: [<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>]
5print(gPolygon.asPolygon())
6# output: [[<QgsPointXY: POINT(1 1)>, <QgsPointXY: POINT(2 2)>, <QgsPointXY: POINT(2 1)>, <QgsPointXY: POINT(1 1)>]]

참고

(x,y) 투플은 실제 투플(tuple)이 아니라 QgsPoint 클래스 객체로, x()y() 메소드를 사용해서 그 값들에 접근할 수 있습니다.

다중 부분 도형의 경우 비슷한 접근자 함수들이 존재합니다: asMultiPoint(), asMultiPolyline(), 그리고 asMultiPolygon() 메소드들입니다.

도형 유형과는 상관없이 도형의 모든 부분들을 반복해서 작업할 수 있습니다. 예를 들면:

geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
for part in geom.parts():
  print(part.asWkt())
Point (0 0)
Point (1 1)
Point (2 2)
geom = QgsGeometry.fromWkt( 'LineString( 0 0, 10 10 )' )
for part in geom.parts():
  print(part.asWkt())
LineString (0 0, 10 10)
gc = QgsGeometryCollection()
gc.fromWkt('GeometryCollection( Point(1 2), Point(11 12), LineString(33 34, 44 45))')
print(gc[1].asWkt())
Point (11 12)

QgsGeometry.parts() 메소드를 사용하면 도형의 각 부분을 수정할 수도 있습니다.

1geom = QgsGeometry.fromWkt( 'MultiPoint( 0 0, 1 1, 2 2)' )
2for part in geom.parts():
3  part.transform(QgsCoordinateTransform(
4    QgsCoordinateReferenceSystem("EPSG:4326"),
5    QgsCoordinateReferenceSystem("EPSG:3111"),
6    QgsProject.instance())
7  )
8
9print(geom.asWkt())
MultiPoint ((-10334728.12541878595948219 -5360106.25905461423099041),(-10462135.16126426123082638 -5217485.4735023295506835),(-10589399.84444035589694977 -5072021.45942386891692877))

7.3. 도형 술어 및 연산

QGIS는 도형 술어(predicate) 같은 고급 도형 연산(contains(), intersects(), …)과 집합 연산(combine(), difference(), …)에 GEOS 라이브러리를 사용합니다. GEOS 라이브러리는 (폴리곤의 경우) 면적 또는 (폴리곤 및 라인의 경우) 길이 같은 도형의 기하 속성도 계산할 수 있습니다.

지정한 레이어에 있는 피처들을 반복 작업해서 피처들의 도형을 바탕으로 도형 계산을 수행하는 예시를 볼까요. 다음 코드는 예제 QGIS 프로젝트 안에서 countries 레이어에 있는 각 국가의 면적과 둘레를 계산해서 출력할 것입니다.

다음 코드는 layer 가 폴리곤 피처 유형인 QgsVectorLayer 클래스 객체라고 가정합니다.

 1# let's access the 'countries' layer
 2layer = QgsProject.instance().mapLayersByName('countries')[0]
 3
 4# let's filter for countries that begin with Z, then get their features
 5query = '"name" LIKE \'Z%\''
 6features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
 7
 8# now loop through the features, perform geometry computation and print the results
 9for f in features:
10  geom = f.geometry()
11  name = f.attribute('NAME')
12  print(name)
13  print('Area: ', geom.area())
14  print('Perimeter: ', geom.length())
1Zambia
2Area:  62.82279065343119
3Perimeter:  50.65232014052552
4Zimbabwe
5Area:  33.41113559136517
6Perimeter:  26.608288555013935

이제 도형들의 면적과 둘레를 계산해서 출력했습니다. 하지만 그 값들이 이상하다는 사실을 한 눈에 알아차렸겠죠. QgsGeometry 클래스에서 나온 area()length() 메소드를 사용해서 면적과 둘레를 계산할 때 좌표계를 연산에 넣지 않았기 때문입니다. 좀 더 강력한 면적 및 거리 계산을 하고 싶다면, 타원체 기반 계산을 수행할 수 있는 QgsDistanceArea 클래스를 사용하면 됩니다:

다음 코드는 layer 가 폴리곤 피처 유형인 QgsVectorLayer 클래스 객체라고 가정합니다.

 1d = QgsDistanceArea()
 2d.setEllipsoid('WGS84')
 3
 4layer = QgsProject.instance().mapLayersByName('countries')[0]
 5
 6# let's filter for countries that begin with Z, then get their features
 7query = '"name" LIKE \'Z%\''
 8features = layer.getFeatures(QgsFeatureRequest().setFilterExpression(query))
 9
10for f in features:
11  geom = f.geometry()
12  name = f.attribute('NAME')
13  print(name)
14  print("Perimeter (m):", d.measurePerimeter(geom))
15  print("Area (m2):", d.measureArea(geom))
16
17  # let's calculate and print the area again, but this time in square kilometers
18  print("Area (km2):", d.convertAreaMeasurement(d.measureArea(geom), QgsUnitTypes.AreaSquareKilometers))
1Zambia
2Perimeter (m): 5539361.250294601
3Area (m2): 751989035032.9031
4Area (km2): 751989.0350329031
5Zimbabwe
6Perimeter (m): 2865021.3325076113
7Area (m2): 389267821381.6008
8Area (km2): 389267.8213816008

아니면, 포인트 2개 사이의 거리를 알고 싶을 수도 있을 겁니다.

 1d = QgsDistanceArea()
 2d.setEllipsoid('WGS84')
 3
 4# Let's create two points.
 5# Santa claus is a workaholic and needs a summer break,
 6# lets see how far is Tenerife from his home
 7santa = QgsPointXY(25.847899, 66.543456)
 8tenerife = QgsPointXY(-16.5735, 28.0443)
 9
10print("Distance in meters: ", d.measureLine(santa, tenerife))

QGIS에 포함되어 있는 알고리즘들의 수많은 예시를 찾아볼 수 있으며, 이 알고리즘들을 사용해서 벡터 데이터를 분석하고 변환할 수 있습니다. 다음 링크들은 그 가운데 몇몇 알고리즘들의 코드를 보여줍니다.