지난 포스팅에서는 shapely 모듈의 기본적인 사용법과 도형을 그리는 방법을 알아보았다. 하지만 shapely는 더 놀라운 기능을 가지고 있다. 이번 포스팅에서는 shapely 모듈로 할 수 있는 일들에 대해서 알아본다.
shapely 모듈 응용하기
1) 원 또는 타원 그리기 : Point
a. 원
shapely는 원(또는 타원)을 나타내는 객체가 없어서 직접적으로 호출할 수는 없다. 하지만 Point 객체를 이용하면 만들 수 있다. 먼저 Point 객체를 생성하여 원의 중심을 설정하고 buffer에 반지름 길이를 넣어주면 원을 만들 수 있다.
아래 그림은 Point를 이용하여 원을 만들고 그림을 그린다.
import matplotlib.pyplot as plt plt.rcParams['axes.unicode_minus'] = False from shapely import Point from shapely.plotting import plot_polygon circle = Point(2,3).buffer(3) ## 중심이 2, 3이고 반지름이 3인 원 min_val = min(circle.bounds) max_val = max(circle.bounds) fig = plt.figure(figsize=(8,8)) fig.set_facecolor('white') ax = fig.add_subplot() plot_polygon(circle, ax=ax, add_points=False) ax.set_xlim(min_val, max_val) ax.set_ylim(min_val, max_val) plt.show()

b. 타원
타원은 원을 그리는 것보다 약간 복잡하다. 먼저 Point 객체와 buffer를 이용하여 단위원(Unit Circle)을 만들고 affinity의 scale을 이용하여 단위원을 x축, y축으로 늘려주면 된다.
from shapely import affinity circle = Point(5,5).buffer(1) ## 중심이 (5, 5)이고 반지름이 1인 원 ellipse = affinity.scale(circle, 5, 10) ## x축 방향으로 5배 y축 방향으로 10배 확대 min_val = min(ellipse.bounds) max_val = max(ellipse.bounds) fig = plt.figure(figsize=(8,8)) fig.set_facecolor('white') ax = fig.add_subplot() plot_polygon(ellipse, ax=ax, add_points=False) ## xlim, ylim을 지정안하면 타원이 원처럼 보임 ax.set_xlim(min_val, max_val) ax.set_ylim(min_val-2, max_val+2) plt.show()

2) 두 도형 간 최소 거리 구하기 : nearest_points
nearest_points를 이용하면 두 도형 간 최소 거리를 계산할 수 있다. nearest_points에 도형을 나타내는 두 객체를 차례대로 넣어주면 각 객체의 바운더리 중에서 최소 거리가 되는 지점을 알려준다. 최소 거리는 도형 객체가 갖고 있는 distance를 이용하여 계산할 수도 있다.
from shapely import LineString from shapely.ops import nearest_points from shapely.plotting import plot_line, plot_polygon circle = Point(5,5).buffer(2) ## 중심이 (5, 5)이고 반지름이 1인 원 ellipse = affinity.scale(Point(0,0).buffer(1), 2, 4) min_distance_line = LineString(nearest_points(circle, ellipse)) print('두 도형간 최소 거리가 되는 점 :', nearest_points(circle, ellipse)) print('두 도형간 최소 거리 :', min_distance_line.length) ## 또는 circle.distance(ellipse)

nearest_points가 두 객체간 거리를 최소로 하는 점 2개를 반환한다는 것을 이용하여 두 도형 간 최소 직선 경로를 그릴 수도 있다.
fig = plt.figure(figsize=(6,6)) fig.set_facecolor('white') ax = fig.add_subplot() plot_polygon(circle, ax=ax, add_points=False) plot_polygon(ellipse, ax=ax, add_points=False) plot_line(min_distance_line, ax=ax) ax.set_xlim(-5, 8) ax.set_ylim(-5, 8) plt.show()

3) 한 도형이 다른 도형 내부에 있는지 확인하기 : contains
shapely는 특정 포인트(점) 또는 도형이 다른 도형 내부에 있는지 확인하는 것도 가능하게 해준다. polygon 객체가 갖고 있는 contains 메서드를 이용하면 된다.
아래 코드는 두 점과 원이 다각형(Polygon) 내부에 있는지 여부를 출력하고 실제로 그림을 통해서 확인한다.
from shapely import Polygon from shapely.plotting import plot_points, plot_polygon p1 = Point(4, 4) p2 = Point(1, 1) circle = Point(3, 2).buffer(0.3) ## 중심이 (3, 2)이고 반지름이 0.3인 원 polygon = Polygon([(0, 0), (3, 4), (5, 2), (3, 1)]) print('점 p1을 포함하는가? :', polygon.contains(p1)) print('점 p2을 포함하는가? :', polygon.contains(p2)) print('원을 포함하는가? :', polygon.contains(circle)) fig = plt.figure(figsize=(6,6)) fig.set_facecolor('white') ax = fig.add_subplot() plot_polygon(polygon, ax=ax, add_points=False) plot_polygon(circle, ax=ax, add_points=False, facecolor='r', edgecolor='none') plot_points([p1, p2], ax=ax, color='k') plt.show()

4) Polygon에서 interior 사용하기
Polygon은 holes 인자를 지정하여 좀 더 풍부한 도형을 표현할 수 있다. 이때 외곽쪽 경계를 나타내는 exterior 포인트는 shell 인자에 내부 경계를 나타내는 interior 포인트는 holes 인자에 넣어주면 된다. 이때 exterior 포인트와 interior 포인트가 서로 교차하지 않도록 해주어야 원하는 대로 잘 그려진다.
아래 코드는 exterior 포인트와 interior 포인트가 교차하지 않는 경우와 교차하는 경우에 대한 그림을 그린 것이다.
from shapely import Polygon from shapely.plotting import plot_points, plot_polygon exterior = [(0, 0), (0, 2), (2, 2), (2, 0), (0, 0)] valid_interior = [(1, 0), (0.5, 0.5), (1, 1), (1.5, 0.5), (1, 0)][::-1] ## exterior와 valid_interior가 교차하지 않음 valid_polygon = Polygon(exterior, [valid_interior]) ## interior 포인트는 리스트로 감싸줘야함. ## exterior와 valid_interior가 교차 invalid_interior = [(1, 0), (0.5, 0.5), (1, 1), (1.5, 0.5), (1, 0)] invalid_polygon = Polygon(exterior, [invalid_interior]) ## interior 포인트는 리스트로 감싸줘야함. fig, axs = plt.subplots(1, 2, figsize=(10,5)) fig.set_facecolor('white') ax1 = axs[0] ax2 = axs[1] plot_polygon(polygon, ax=ax1) ax1.set_title('Valid Polygon') plot_polygon(invalid_polygon, ax=ax2) ax2.set_title('Invalid Polygon') plt.show()

이러한 차이가 발생한 이유는 valid_polygon 객체(아래 그림 왼쪽)는 아래와 같은 진행 방향으로 exterior와 interior 포인트를 지정했고 서로 교차가 되지 않기 때문에 자연스럽게 빈 공간(hole)이 생긴 것이다. 반면 invalid_polygon 객체(아래 그림 오른쪽)은 4->5 경로와 8->9 경로가 서로 교차하므로 빈 공간이 생기지 않은 것이다.


4) 도형 병합, 빼기, 교집합: union, difference, intersection
나는 개인적으로 이 부분이 가장 강력한 기능이라고 생각한다. shapely는 두 도형의 병합, 빼기, 교집합에 대응하는 도형을 생성할 수 있다. 병합은 union, 빼기는 difference, 교집합은 intersection을 이용하면 된다.
아래 코드는 두 원에 대한 병합, 빼기 그리고 교집합에 대응하는 도형을 구하고 이를 그린 것이다.
from shapely import Point from shapely.plotting import plot_polygon left_circle = Point(0,0).buffer(3) right_circle = Point(2.5,0).buffer(3) union_circles = left_circle.union(right_circle) ## 도형 병합 left_right_circle = left_circle.difference(right_circle) ## 왼쪽에서 오른쪽 빼기 right_left_circle = right_circle.difference(left_circle) ## 오른쪽에서 왼쪽 빼기 intersection_circles = left_circle.intersection(right_circle) ## 교집합 fig, axs = plt.subplots(2, 2, figsize=(8,8)) fig.set_facecolor('white') ax1 = axs[0, 0] ax2 = axs[0, 1] ax3 = axs[1, 0] ax4 = axs[1, 1] plot_polygon(left_right_circle, ax=ax1, add_points=False) plot_polygon(right_left_circle, ax=ax2, add_points=False) plot_polygon(union_circles, ax=ax3, add_points=False) plot_polygon(intersection_circles, ax=ax4, add_points=False) total_axs = [ax1, ax2, ax3, ax4] titles = ['left-right', 'right-left', 'union', 'intersection'] min_x, max_x = -4, 6 min_y, max_y = -5, 5 for i, ax in enumerate(total_axs): ax.set_xlim(min_x, max_x) ax.set_ylim(min_y, max_y) ax.set_title(titles[i]) plt.show()

5) 두 도형이 겹치는지 여부 : disjoint
shapely 객체가 가진 disjoint를 이용하면 두 도형이 겹치는지 안겹치는지 알 수 있다. disjoint를 이용하여 True가 나오면 안 겹치고 False가 나오면 겹치는 것을 의미한다.
아래 코드는 원 세개를 생성하고 두 원에 대한 겹침 여부를 출력하고 이를 그림으로 확인한다.
from shapely import Point from shapely.plotting import plot_polygon c1 = Point(0,0).buffer(3) c2 = Point(2.5,0).buffer(3) c3 = Point(5,5).buffer(3) print('c1과 c2는 겹치는가? :', not c1.disjoint(c2)) print('c1과 c3는 겹치는가? :', not c1.disjoint(c3)) fig, axs = plt.subplots(1, 2, figsize=(10,5)) fig.set_facecolor('white') ax1 = axs[0] ax2 = axs[1] plot_polygon(c1, ax=ax1, add_points=False) plot_polygon(c2, ax=ax1, add_points=False) plot_polygon(c1, ax=ax2, add_points=False) plot_polygon(c3, ax=ax2, add_points=False) ax1.set_title('c1, c2') ax2.set_title('c1, c3') plt.show()

'프로그래밍 > 기타 Python 모듈' 카테고리의 다른 글
factor_analyzer 모듈을 이용한 인자 분석 (0) | 2023.05.06 |
---|---|
shapely 모듈에 대해서 알아보자 - 기본편 (0) | 2023.03.29 |
dateutil 모듈을 이용하여 datetime 객체 다루기 (0) | 2023.03.25 |
[XGBoost] XGBoost의 개별 트리로부터 여러 정보(변수 출현 빈도, 예측) 계산하기(feat. get_boost) (0) | 2023.03.24 |
Pyinstaller 기본 - 이용하여 파이썬(.py) 파일을 실행 파일(.exe)로 만들기 (1) | 2023.03.17 |
댓글