Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
__pycache__/
*.py[cod]
*.egg-info/
.pytest_cache/
dist/
build/
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 pyncmap contributors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
64 changes: 58 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,63 @@
# pyNCmap
# pyncmap

Use NCL colortable in python matplotlib colormap without install new packages
NCL 스타일(`*.rgb`, `*.bound`) colormap 파일을 Python Matplotlib에서 바로 사용할 수 있게 해주는 라이브러리입니다.

All colortable in NCL exist in cmap_data.tar.gz (Ref: https://www.ncl.ucar.edu/Document/Graphics/color_table_gallery.shtml)
## 설치

Unzip the compressed file and place on the same directory with the python code
```bash
pip install pyncmap
```

No compatibility issue
로컬 개발 모드:

You can use reversed colormap for any colortable just by typing "_r" after the colortable name (i.e., rainbow_r)
```bash
pip install -e .
```

## 사용법

```python
import matplotlib.pyplot as plt
import numpy as np
from pyncmap import ColorMapMaker

data = np.random.rand(50, 50) * 60

cm = ColorMapMaker("KMA_radar")
cmap, norm, boundaries = cm.get_all()

plt.pcolormesh(data, cmap=cmap, norm=norm)
plt.colorbar(boundaries=boundaries)
plt.show()
```

reverse colormap은 이름 끝에 `_r`를 붙여 사용합니다.

```python
cm = ColorMapMaker("KMA_radar_r")
```

## API

- `ColorMapMaker(name, data_dir=None)`
- `get_colormap(name, data_dir=None)` → `(cmap, norm, boundaries)`
- `list_available_colormaps(data_dir=None)`

## 포함 데이터

현재 저장소에는 예시로 KMA 계열 colormap 데이터가 포함되어 있습니다.

- `KMA_radar`: 기상청 레이더 관측 및 초단기 예보 자료에 사용하는 colormap
- `KMA_precip1224`: 기상청 AWS 관측(12시간/24시간 누적 강수 관측)에 사용하는 colormap
- `KMA_precip1560`: 기상청 AWS 관측(15분 관측/60분 누적 강수 관측)에 사용하는 colormap

## 개발

```bash
pytest
python -m build
```

## 레거시 호환

기존 `ncl_colormap.py` import도 유지되며, 내부적으로 `pyncmap` 패키지를 사용합니다.
Binary file removed __pycache__/ncl_colormap.cpython-39.pyc
Binary file not shown.
64 changes: 0 additions & 64 deletions cmap_data/KMA_radar_old.rgb

This file was deleted.

98 changes: 5 additions & 93 deletions ncl_colormap.py
Original file line number Diff line number Diff line change
@@ -1,96 +1,8 @@
import os
import numpy as np
from matplotlib.colors import ListedColormap, BoundaryNorm
"""Backward-compatible shim for legacy imports.

class ColorMapMaker:
"""
Colormap 이름과 데이터 경로를 기반으로 Matplotlib의 컬러맵,
정규화(norm), 경계(boundaries)를 관리하는 클래스입니다.
Use ``from pyncmap import ColorMapMaker`` in new code.
"""

사용 예시:
>>> cbar = ColorMapMaker('my_cmap_r')
>>> cmap = cbar.cmap
>>> norm = cbar.norm
>>> boundaries = cbar.boundaries
>>> # 또는 한 번에 가져오기
>>> cmap, norm, boundaries = cbar.get_all()
>>> plt.pcolormesh(data, cmap=cmap, norm=norm)
"""
def __init__(self, name: str, data_dir: str = None):
"""
Args:
name (str): 불러올 컬러맵의 이름. 이름 끝에 '_r'을 붙이면 색상 순서가 반전됩니다.
data_dir (str, optional): cmap 데이터 파일(.rgb, .bound)이 있는
디렉토리 경로. 지정하지 않으면 이 파일과
동일한 경로의 'cmap_data' 폴더를 사용합니다.
"""
self.name = name
self._reverse = name.endswith('_r')
self._base_name = name[:-2] if self._reverse else name
from pyncmap import ColorMapMaker, get_colormap, list_available_colormaps

if data_dir is None:
# 스크립트가 실행되는 위치를 기준으로 'cmap_data' 폴더 경로를 설정합니다.
cwd = os.path.dirname(os.path.abspath(__file__))
self._data_path = os.path.join(cwd, 'cmap_data')
else:
self._data_path = data_dir

# 결과를 캐싱하기 위한 내부 변수
self._cmap = None
self._boundaries = None
self._norm = None

@staticmethod
def _is_float(element: any) -> bool:
"""문자열이 float으로 변환 가능한지 확인하는 정적 헬퍼 메서드"""
try:
float(element)
return True
except ValueError:
return False

@property
def cmap(self) -> ListedColormap:
"""컬러맵 객체 (ListedColormap)를 반환합니다. 첫 호출 시 파일을 읽어 생성합니다."""
if self._cmap is None:
rgb_file = os.path.join(self._data_path, f'{self._base_name}.rgb')
with open(rgb_file, 'r') as f:
lines = f.readlines()

li_rgb = []
for line in lines:
# 공백으로 분리된 숫자들을 float 리스트로 변환
colors = [float(s) for s in line.split() if self._is_float(s)]
if len(colors) == 3:
li_rgb.append(colors)

if self._reverse:
li_rgb = list(reversed(li_rgb))

data = np.array(li_rgb)
# 데이터를 최대값으로 나누어 0-1 사이로 정규화
data = data / np.max(data)
self._cmap = ListedColormap(data, name=self.name)
return self._cmap

@property
def boundaries(self) -> list:
"""경계 값 리스트를 반환합니다. 첫 호출 시 파일을 읽어 생성합니다."""
if self._boundaries is None:
bound_file = os.path.join(self._data_path, f'{self._base_name}.bound')
with open(bound_file, 'r') as f:
# list comprehension을 사용하여 더 간결하게 작성
self._boundaries = [float(line.strip()) for line in f]
return self._boundaries

@property
def norm(self) -> BoundaryNorm:
"""정규화 객체 (BoundaryNorm)를 반환합니다. 첫 호출 시 생성합니다."""
if self._norm is None:
# .cmap과 .boundaries 속성을 사용하여 BoundaryNorm 객체 생성
self._norm = BoundaryNorm(self.boundaries, self.cmap.N, clip=True)
return self._norm

def get_all(self) -> tuple:
"""컬러맵, 정규화 객체, 경계 리스트를 튜플로 한 번에 반환합니다."""
return self.cmap, self.norm, self.boundaries
__all__ = ["ColorMapMaker", "get_colormap", "list_available_colormaps"]
39 changes: 39 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[build-system]
requires = ["setuptools>=68", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "pyncmap"
version = "0.1.0"
description = "Load NCL-style RGB colormaps in Matplotlib"
readme = "README.md"
requires-python = ">=3.9"
license = { text = "MIT" }
authors = [{ name = "pyncmap contributors" }]
dependencies = [
"matplotlib>=3.5",
"numpy>=1.21",
]
keywords = ["ncl", "matplotlib", "colormap", "visualization"]
classifiers = [
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only",
"License :: OSI Approved :: MIT License",
"Intended Audience :: Science/Research",
"Topic :: Scientific/Engineering :: Visualization",
]

[project.urls]
Homepage = "https://github.com/example/pyncmap"

[tool.setuptools]
package-dir = {"" = "src"}

[tool.setuptools.packages.find]
where = ["src"]

[tool.setuptools.package-data]
pyncmap = ["cmap_data/*.rgb", "cmap_data/*.bound"]

[tool.pytest.ini_options]
testpaths = ["tests"]
5 changes: 5 additions & 0 deletions src/pyncmap/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""pyNCmap: Load NCL-style RGB colormaps for Matplotlib."""

from .core import ColorMapMaker, get_colormap, list_available_colormaps

__all__ = ["ColorMapMaker", "get_colormap", "list_available_colormaps"]
32 changes: 32 additions & 0 deletions src/pyncmap/cmap_data/KMA_precip1224.bound
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
0
1
2
4
6
8
10
12
15
20
25
30
40
50
60
70
80
90
100
110
130
150
200
250
300
350
400
500
600
700
800
900
34 changes: 34 additions & 0 deletions src/pyncmap/cmap_data/KMA_precip1224.rgb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
ncolors=31
# r g b
255 255 255
232 232 155
220 220 106
209 209 57
169 169 38
120 120 27
155 232 155
106 220 106
57 209 57
38 169 38
27 120 27
155 232 230
106 220 218
57 209 206
38 169 166
27 120 118
155 155 232
106 106 220
57 57 209
38 38 169
27 27 120
232 155 232
220 106 220
209 57 209
169 38 169
120 27 120
232 155 155
220 106 106
209 57 57
169 38 38
169 38 38
40 40 40
Loading