소개
이 랩에서는 Matplotlib 을 사용하여 사용자 정의 투영법을 만드는 방법을 배웁니다. Matplotlib 의 많은 기능을 활용하여 해머 투영법 (Hammer projection) 을 시연할 것입니다. 프로그래밍 언어로는 Python 을 사용합니다.
VM 팁
VM 시작이 완료되면, 왼쪽 상단 모서리를 클릭하여 Notebook 탭으로 전환하여 실습을 위해 Jupyter Notebook에 접속하십시오.
때로는 Jupyter Notebook 이 로딩을 완료하는 데 몇 초 정도 기다려야 할 수 있습니다. Jupyter Notebook 의 제한으로 인해 작업의 유효성 검사는 자동화될 수 없습니다.
학습 중에 문제가 발생하면 언제든지 Labby 에게 문의하십시오. 세션 후 피드백을 제공해주시면 문제를 신속하게 해결해 드리겠습니다.
라이브러리 임포트
먼저, 사용자 정의 투영법을 생성하는 데 필요한 라이브러리를 임포트합니다.
import numpy as np
import matplotlib
from matplotlib.axes import Axes
import matplotlib.axis as maxis
from matplotlib.patches import Circle
from matplotlib.path import Path
from matplotlib.projections import register_projection
import matplotlib.spines as mspines
from matplotlib.ticker import FixedLocator, Formatter, NullLocator
from matplotlib.transforms import Affine2D, BboxTransformTo, Transform
GeoAxes 클래스 생성
GeoAxes라는 지리 투영법 (geographic projections) 을 위한 추상 기본 클래스를 생성합니다.
class GeoAxes(Axes):
"""
지리 투영법을 위한 추상 기본 클래스
"""
class ThetaFormatter(Formatter):
"""
세타 눈금 레이블의 형식을 지정하는 데 사용됩니다. 네이티브
단위인 라디안을 도 (degree) 로 변환하고 도 기호를 추가합니다.
"""
def __init__(self, round_to=1.0):
self._round_to = round_to
def __call__(self, x, pos=None):
degrees = round(np.rad2deg(x) / self._round_to) * self._round_to
return f"{degrees:0.0f}\N{DEGREE SIGN}"
RESOLUTION = 75
def _init_axis(self):
self.xaxis = maxis.XAxis(self)
self.yaxis = maxis.YAxis(self)
## GeoAxes.xaxis.clear() 가 작동할 때까지,
## Axes._init_axis() 에서 수행되는 것처럼, xaxis 또는 yaxis 를 스파인 (spines) 에 등록하지 마십시오.
## self.spines['geo'].register_axis(self.yaxis)
def clear(self):
## docstring 상속
super().clear()
self.set_longitude_grid(30)
self.set_latitude_grid(15)
self.set_longitude_grid_ends(75)
self.xaxis.set_minor_locator(NullLocator())
self.yaxis.set_minor_locator(NullLocator())
self.xaxis.set_ticks_position('none')
self.yaxis.set_ticks_position('none')
self.yaxis.set_tick_params(label1On=True)
## 왜 y 축 눈금 레이블을 켜야 하지만,
## x 축 눈금 레이블은 이미 켜져 있습니까?
self.grid(rcParams['axes.grid'])
Axes.set_xlim(self, -np.pi, np.pi)
Axes.set_ylim(self, -np.pi / 2.0, np.pi / 2.0)
HammerAxes 클래스 생성
HammerAxes라고 불리는 등면적 지도 투영법인 Aitoff-Hammer 투영법을 위한 사용자 정의 클래스를 생성합니다.
class HammerAxes(GeoAxes):
"""
Aitoff-Hammer 투영법, 등면적 지도 투영법을 위한 사용자 정의 클래스.
https://en.wikipedia.org/wiki/Hammer_projection
"""
## 투영법은 이름을 지정해야 합니다. 이것은
## 사용자가 투영법을 선택하는 데 사용됩니다.
## 즉, ``subplot(projection='custom_hammer')``.
name = 'custom_hammer'
class HammerTransform(Transform):
"""기본 Hammer 변환."""
input_dims = output_dims = 2
def __init__(self, resolution):
"""
새로운 Hammer 변환을 생성합니다. 해상도는 각 입력 선분 사이를 보간하여
곡선 Hammer 공간에서 경로를 근사화하는 단계 수입니다.
"""
Transform.__init__(self)
self._resolution = resolution
def transform_non_affine(self, ll):
longitude, latitude = ll.T
## 일부 값 미리 계산
half_long = longitude / 2
cos_latitude = np.cos(latitude)
sqrt2 = np.sqrt(2)
alpha = np.sqrt(1 + cos_latitude * np.cos(half_long))
x = (2 * sqrt2) * (cos_latitude * np.sin(half_long)) / alpha
y = (sqrt2 * np.sin(latitude)) / alpha
return np.column_stack([x, y])
def transform_path_non_affine(self, path):
## vertices = path.vertices
ipath = path.interpolated(self._resolution)
return Path(self.transform(ipath.vertices), ipath.codes)
def inverted(self):
return HammerAxes.InvertedHammerTransform(self._resolution)
class InvertedHammerTransform(Transform):
input_dims = output_dims = 2
def __init__(self, resolution):
Transform.__init__(self)
self._resolution = resolution
def transform_non_affine(self, xy):
x, y = xy.T
z = np.sqrt(1 - (x / 4) ** 2 - (y / 2) ** 2)
longitude = 2 * np.arctan((z * x) / (2 * (2 * z ** 2 - 1)))
latitude = np.arcsin(y*z)
return np.column_stack([longitude, latitude])
def inverted(self):
return HammerAxes.HammerTransform(self._resolution)
def __init__(self, *args, **kwargs):
self._longitude_cap = np.pi / 2.0
super().__init__(*args, **kwargs)
self.set_aspect(0.5, adjustable='box', anchor='C')
self.clear()
def _get_core_transform(self, resolution):
return self.HammerTransform(resolution)
투영법 등록
이제 사용자가 선택할 수 있도록 Matplotlib 에 투영법을 등록합니다.
register_projection(HammerAxes)
예제 생성
마지막으로, 사용자 정의 투영법을 사용하여 예제를 생성합니다.
if __name__ == '__main__':
import matplotlib.pyplot as plt
## 이제 사용자 정의 투영법을 사용하여 간단한 예제를 만듭니다.
fig, ax = plt.subplots(subplot_kw={'projection': 'custom_hammer'})
ax.plot([-1, 1, 1], [-1, -1, 1], "o-")
ax.grid()
plt.show()
요약
이 랩에서는 Matplotlib 을 사용하여 사용자 정의 투영법을 만드는 방법을 배웠습니다. Matplotlib 의 많은 기능을 활용하여 Hammer 투영법이라는 사용자 정의 투영법을 만들었습니다. GeoAxes 클래스, HammerAxes 클래스를 생성하고, 투영법을 등록하며, 사용자 정의 투영법을 사용하여 예제를 만드는 방법을 배웠습니다.