rang 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- rang/__init__.py +192 -0
- rang/_palettes.py +14 -0
- rang-0.2.0.dist-info/METADATA +104 -0
- rang-0.2.0.dist-info/RECORD +8 -0
- rang-0.2.0.dist-info/WHEEL +5 -0
- rang-0.2.0.dist-info/licenses/LICENSE +21 -0
- rang-0.2.0.dist-info/licenses/LICENSE-CC0.txt +11 -0
- rang-0.2.0.dist-info/top_level.txt +1 -0
rang/__init__.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Rang, color palettes from Persian art.
|
|
2
|
+
|
|
3
|
+
Palettes are stored as a ramp of hex colors plus a pick order. Asking for a
|
|
4
|
+
few colors returns a well separated subset, asking for more than the palette
|
|
5
|
+
holds interpolates along the ramp.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import operator
|
|
9
|
+
|
|
10
|
+
from ._palettes import PALETTES
|
|
11
|
+
|
|
12
|
+
__version__ = "0.2.0"
|
|
13
|
+
__all__ = ["rang", "cmap", "list_palettes", "source", "colorblind_friendly",
|
|
14
|
+
"register", "registered_name", "set_palette", "PALETTES"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def list_palettes(colorblind_only=False):
|
|
18
|
+
"""Names of the available palettes, sorted."""
|
|
19
|
+
names = sorted(PALETTES)
|
|
20
|
+
if colorblind_only:
|
|
21
|
+
names = [n for n in names if PALETTES[n]["colorblind"]]
|
|
22
|
+
return names
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _get(name):
|
|
26
|
+
if name not in PALETTES:
|
|
27
|
+
raise KeyError(f"Unknown palette {name!r}. "
|
|
28
|
+
f"Available: {', '.join(sorted(PALETTES))}")
|
|
29
|
+
return PALETTES[name]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _hex_to_rgb(h):
|
|
33
|
+
h = h.lstrip("#")
|
|
34
|
+
return tuple(int(h[i:i + 2], 16) for i in (0, 2, 4))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _positive_integer(value):
|
|
38
|
+
if isinstance(value, bool):
|
|
39
|
+
raise TypeError("n must be an integer")
|
|
40
|
+
try:
|
|
41
|
+
value = operator.index(value)
|
|
42
|
+
except TypeError as exc:
|
|
43
|
+
raise TypeError("n must be an integer") from exc
|
|
44
|
+
if value < 1:
|
|
45
|
+
raise ValueError("n must be at least 1")
|
|
46
|
+
return value
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _check_direction(direction):
|
|
50
|
+
if direction not in (1, -1):
|
|
51
|
+
raise ValueError("direction must be 1 or -1")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def _interpolate(colors, n):
|
|
55
|
+
if n == 1:
|
|
56
|
+
return [colors[0]]
|
|
57
|
+
rgbs = [_hex_to_rgb(c) for c in colors]
|
|
58
|
+
out = []
|
|
59
|
+
for i in range(n):
|
|
60
|
+
t = i * (len(colors) - 1) / (n - 1)
|
|
61
|
+
j = min(int(t), len(colors) - 2)
|
|
62
|
+
f = t - j
|
|
63
|
+
rgb = tuple(round(a + (b - a) * f) for a, b in zip(rgbs[j], rgbs[j + 1]))
|
|
64
|
+
out.append("#{:02x}{:02x}{:02x}".format(*rgb))
|
|
65
|
+
return out
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def rang(name, n=None, kind=None, direction=1, override_order=False):
|
|
69
|
+
"""Return a list of hex colors from a palette.
|
|
70
|
+
|
|
71
|
+
Parameters
|
|
72
|
+
----------
|
|
73
|
+
name : palette name, see list_palettes()
|
|
74
|
+
n : how many colors, defaults to the full palette
|
|
75
|
+
kind : "discrete" or "continuous". Defaults to discrete while n fits the
|
|
76
|
+
palette and continuous beyond that.
|
|
77
|
+
direction : 1 for the stored order, -1 to reverse
|
|
78
|
+
override_order : take the first n ramp colors instead of the stored
|
|
79
|
+
pick order
|
|
80
|
+
"""
|
|
81
|
+
pal = _get(name)
|
|
82
|
+
colors, order = list(pal["colors"]), list(pal["order"])
|
|
83
|
+
if n is None:
|
|
84
|
+
n = len(colors)
|
|
85
|
+
n = _positive_integer(n)
|
|
86
|
+
_check_direction(direction)
|
|
87
|
+
if kind is None:
|
|
88
|
+
kind = "continuous" if n > len(colors) else "discrete"
|
|
89
|
+
if kind not in ("discrete", "continuous"):
|
|
90
|
+
raise ValueError("kind must be 'discrete' or 'continuous'")
|
|
91
|
+
|
|
92
|
+
if kind == "discrete":
|
|
93
|
+
if n > len(colors):
|
|
94
|
+
raise ValueError(f"{name} holds {len(colors)} colors, "
|
|
95
|
+
f"use kind='continuous' for more")
|
|
96
|
+
out = colors[:n] if override_order else [c for c, r in zip(colors, order) if r <= n]
|
|
97
|
+
else:
|
|
98
|
+
out = _interpolate(colors, n)
|
|
99
|
+
return out[::-1] if direction == -1 else out
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def cmap(name, n=None, kind="continuous", direction=1):
|
|
103
|
+
"""A matplotlib colormap built from the palette.
|
|
104
|
+
|
|
105
|
+
Parameters
|
|
106
|
+
----------
|
|
107
|
+
name : palette name, see list_palettes()
|
|
108
|
+
n : leave empty for a smooth colormap. Give a number to get that many
|
|
109
|
+
fixed steps instead, which suits classified rasters and choropleths.
|
|
110
|
+
kind : how the n steps are chosen. "continuous" samples evenly along the
|
|
111
|
+
ramp and is what ordered data usually wants. "discrete" follows the
|
|
112
|
+
stored pick order, which is meant for categories.
|
|
113
|
+
direction : 1 for the stored order, -1 to reverse
|
|
114
|
+
|
|
115
|
+
Needs matplotlib, which installs with `pip install rang[plots]`.
|
|
116
|
+
"""
|
|
117
|
+
_check_direction(direction)
|
|
118
|
+
from matplotlib.colors import LinearSegmentedColormap, ListedColormap
|
|
119
|
+
|
|
120
|
+
if n is None:
|
|
121
|
+
colors = list(_get(name)["colors"])
|
|
122
|
+
if direction == -1:
|
|
123
|
+
colors = colors[::-1]
|
|
124
|
+
return LinearSegmentedColormap.from_list(name, colors)
|
|
125
|
+
|
|
126
|
+
colors = rang(name, n, kind, direction=direction)
|
|
127
|
+
return ListedColormap(colors, name=f"{name}_{len(colors)}")
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def registered_name(name, reverse=False):
|
|
131
|
+
"""The matplotlib lookup name for a palette, such as 'rang:Kashan'."""
|
|
132
|
+
_get(name)
|
|
133
|
+
return f"rang:{name}{'_r' if reverse else ''}"
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def register(force=False):
|
|
137
|
+
"""Add every palette to matplotlib's colormap registry.
|
|
138
|
+
|
|
139
|
+
After calling this, any library that accepts a colormap name works with
|
|
140
|
+
Rang without knowing Rang exists. That covers xarray, geopandas,
|
|
141
|
+
rioxarray, seaborn and plain matplotlib.
|
|
142
|
+
|
|
143
|
+
import rang
|
|
144
|
+
rang.register()
|
|
145
|
+
data.plot(cmap="rang:Termeh")
|
|
146
|
+
|
|
147
|
+
Each palette registers twice, once forward and once reversed with an
|
|
148
|
+
"_r" suffix, matching the matplotlib convention.
|
|
149
|
+
|
|
150
|
+
Calling this again is safe and quiet. Names already in the registry are
|
|
151
|
+
left alone unless force is True, which replaces them.
|
|
152
|
+
|
|
153
|
+
Returns the list of Rang names in the registry.
|
|
154
|
+
"""
|
|
155
|
+
import matplotlib
|
|
156
|
+
|
|
157
|
+
names = []
|
|
158
|
+
for key in PALETTES:
|
|
159
|
+
for reverse in (False, True):
|
|
160
|
+
lookup = registered_name(key, reverse)
|
|
161
|
+
names.append(lookup)
|
|
162
|
+
if lookup in matplotlib.colormaps:
|
|
163
|
+
if not force:
|
|
164
|
+
continue
|
|
165
|
+
matplotlib.colormaps.unregister(lookup)
|
|
166
|
+
matplotlib.colormaps.register(
|
|
167
|
+
cmap(key, direction=-1 if reverse else 1), name=lookup
|
|
168
|
+
)
|
|
169
|
+
return names
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def set_palette(name, n=None, direction=1):
|
|
173
|
+
"""Use a palette for the default line and patch colors in matplotlib.
|
|
174
|
+
|
|
175
|
+
Sets the axes property cycle, so plots drawn afterwards pick up the
|
|
176
|
+
palette without naming a color on every call. Returns the colors used.
|
|
177
|
+
"""
|
|
178
|
+
import matplotlib.pyplot as plt
|
|
179
|
+
|
|
180
|
+
colors = rang(name, n, direction=direction)
|
|
181
|
+
plt.rcParams["axes.prop_cycle"] = plt.cycler(color=colors)
|
|
182
|
+
return colors
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def source(name):
|
|
186
|
+
"""Provenance of the artwork behind a palette."""
|
|
187
|
+
return dict(_get(name)["source"])
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def colorblind_friendly(name):
|
|
191
|
+
"""Stored separation flag based on Rang's cutoff of 8."""
|
|
192
|
+
return bool(_get(name)["colorblind"])
|
rang/_palettes.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Palette data written by tools/build.py. Edit palettes/*.json instead."""
|
|
2
|
+
|
|
3
|
+
PALETTES = {
|
|
4
|
+
"Kashan": {'colors': ('#7f3020', '#ab4a47', '#c07049', '#c59b46', '#ccac7e', '#e2cfb1', '#8a9463', '#345f72', '#1a3b45'), 'order': (3, 6, 4, 8, 7, 1, 9, 5, 2), 'colorblind': False, 'persian': 'کاشان', 'pronunciation': 'kah-SHAHN', 'source': {'title': 'Silk Kashan Carpet', 'artist': '', 'date': '16th century', 'geography': 'Made in Iran, probably Kashan', 'medium': 'Silk (warp, weft and pile), asymmetrically knotted pile', 'museum': 'The Metropolitan Museum of Art, New York', 'department': 'Islamic Art', 'accession': '58.46', 'credit': 'Gift of Mrs. Douglas M. Moffat, 1958', 'url': 'https://www.metmuseum.org/art/collection/search/451470', 'image': 'https://images.metmuseum.org/CRDImages/is/original/DT5450.jpg', 'card_image': 'sources/kashan/card.jpg', 'public_domain': True}},
|
|
5
|
+
"Golestan": {'colors': ('#432f2c', '#ae6259', '#b57f86', '#b5b5ac', '#cbb11c', '#9a9a68', '#45939c', '#577ab1', '#333a80'), 'order': (4, 5, 8, 6, 1, 7, 3, 9, 2), 'colorblind': True, 'persian': 'گلستان', 'pronunciation': 'goh-leh-STAHN, Persian for rose garden', 'source': {'title': 'Hunting-scene tile panel at Golestan Palace', 'artist': '', 'date': 'photographed 2018', 'geography': 'Tehran, Iran', 'medium': 'Glazed polychrome tilework', 'site': 'Golestan Palace, UNESCO World Heritage Site', 'reference_label': 'Site reference', 'url': 'https://whc.unesco.org/en/list/1422/', 'image': 'sources/golestan/tilework.jpg', 'context_image': 'sources/golestan/palace.jpg', 'context_caption': 'The panel in place at the palace, with a resident cat', 'credit': 'Photo by Mohsen Tahmasebi Nasab, 2018', 'public_domain': False, 'rights': 'Copyright Mohsen Tahmasebi Nasab, all rights reserved'}},
|
|
6
|
+
"Termeh": {'colors': ('#e5f0ee', '#c8dfe3', '#a9ccd7', '#7fb5c1', '#5d95a8', '#3e738e', '#274e68'), 'order': (1, 7, 5, 6, 3, 4, 2), 'colorblind': False, 'persian': 'ترمه', 'pronunciation': 'tehr-MEH', 'source': {'title': 'Termeh cloth with boteh motifs', 'artist': '', 'date': 'photographed 2026', 'geography': 'Iran', 'medium': 'Textile', 'site': 'Yazd textile tradition', 'reference_label': 'Tradition reference', 'url': 'https://asia-archive.si.edu/object/S2017.14/', 'image': 'sources/termeh/cloth.jpg', 'context_image': 'sources/termeh/setting.jpg', 'context_caption': 'Termeh at rest, a draped cloth, runners and a covered chest', 'credit': 'Photo by Mohsen Tahmasebi Nasab, 2026', 'public_domain': False, 'rights': 'Copyright Mohsen Tahmasebi Nasab, all rights reserved'}},
|
|
7
|
+
"Khatam": {'colors': ('#1c110b', '#542020', '#8d310e', '#bf480d', '#b27a3c', '#c58c51', '#d9a545', '#dbba94', '#e5c870'), 'order': (1, 6, 5, 3, 8, 4, 7, 9, 2), 'colorblind': False, 'persian': 'خاتم', 'pronunciation': 'khaw-TAM', 'source': {'title': 'Khatam panel with brass stars', 'citation': 'Khatam panel with brass stars.', 'artist': '', 'date': '2026', 'geography': 'Iran', 'medium': 'Wood, bone and brass marquetry', 'site': 'Khatam marquetry tradition', 'reference_label': 'Craft history', 'url': 'https://www.iranicaonline.org/articles/isfahan-xiii-crafts/', 'image': 'sources/khatam/inlay.jpg', 'context_image': 'sources/khatam/bazaar.jpg', 'context_caption': 'A handicraft shop in the Isfahan bazaar, 2018, khatam boxes stacked in front of minakari enamel and metalwork', 'credit': 'Photos by Mohsen Tahmasebi Nasab, 2018 and 2026', 'public_domain': False, 'rights': 'Copyright Mohsen Tahmasebi Nasab, all rights reserved'}},
|
|
8
|
+
"Nasir": {'colors': ('#261410', '#3b1261', '#4027e1', '#518ffd', '#74ecf9', '#50a877', '#6dd96f', '#ebc05c', '#f04a23'), 'order': (1, 7, 3, 5, 2, 8, 9, 4, 6), 'colorblind': False, 'persian': 'نصیر', 'pronunciation': 'nah-SEER, from Nasir al-Mulk Mosque in Shiraz', 'source': {'title': 'Stained-glass window at Nasir al-Mulk Mosque', 'artist': 'Tpmehdi', 'date': '2018', 'geography': 'Shiraz, Iran', 'medium': 'Digital photograph', 'site': 'Nasir al-Mulk Mosque', 'reference_label': 'Wikimedia Commons source', 'url': 'https://commons.wikimedia.org/wiki/File:DSC_0277-%D9%85%D8%B3%D8%AC%D8%AF_%D9%86%D8%B5%DB%8C%D8%B1%D8%A7%D9%84%D9%85%D9%84%DA%A9.jpg', 'image': 'sources/nasir/window.jpg', 'context_image': 'sources/nasir/interior.jpg', 'context_caption': 'Sunlight and stained glass reflected across the prayer hall of Nasir al-Mulk Mosque', 'context_url': 'https://commons.wikimedia.org/wiki/File:Nasir_al-_mulk_mosque%2C_Shiraz.jpg', 'context_credit': 'Photograph by MohammadReza Domiri Ganji, 2013', 'context_rights': 'Licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)', 'credit': 'Photograph by Tpmehdi, 2018', 'public_domain': False, 'rights': 'Licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). Rang gallery images use cropped and resized adaptations'}},
|
|
9
|
+
"Mina": {'colors': ('#581c1d', '#984946', '#c19498', '#e7f2f6', '#9ecaee', '#56adee', '#4090a2', '#193caa', '#07187b'), 'order': (5, 3, 7, 1, 6, 9, 4, 8, 2), 'colorblind': True, 'persian': 'مینا', 'pronunciation': 'mee-NAH, Persian for enamel', 'source': {'title': 'Lidded enamel vessel', 'artist': '', 'date': 'photographed 2026', 'geography': 'Iran', 'medium': 'Painted enamel on metal', 'site': 'Persian enamelwork tradition', 'reference_label': 'Craft history', 'url': 'https://www.iranicaonline.org/articles/enamel/', 'image': 'sources/mina/piece.jpg', 'context_image': 'sources/mina/context.jpg', 'context_caption': 'Minakari enamelware displayed in an Isfahan shop', 'context_url': 'https://commons.wikimedia.org/wiki/File:Iranian_vitreous_enamel_(cropped).JPG', 'context_credit': 'Photograph by Wikimedia Commons user مانفی, 2012. Cropped by Joalbertine, 2020', 'context_rights': 'Licensed under [CC BY-SA 3.0](https://creativecommons.org/licenses/by-sa/3.0/)', 'credit': 'Photo by Mohsen Tahmasebi Nasab, 2026', 'public_domain': False, 'rights': 'Copyright Mohsen Tahmasebi Nasab, all rights reserved'}},
|
|
10
|
+
"Rostan": {'colors': ('#4b241e', '#603d34', '#883c3e', '#aa3a41', '#d63d3b', '#d26c55', '#d98665', '#c0947d', '#d2b6a1'), 'order': (1, 9, 5, 8, 3, 7, 4, 6, 2), 'colorblind': False, 'persian': 'رستن', 'pronunciation': 'rohs-TAN, from the Persian title Az In Gooneh Rostan', 'source': {'title': 'Growing in thus Way', 'note': "The English title follows the wording on the artist's official website", 'citation': 'Growing in thus Way, 1972, 140 x 140 cm.', 'artist': 'Iran Darroudi', 'artist_url': 'https://www.irandarroudi.com/en/biography', 'date': '1972', 'dimensions': '140 x 140 cm', 'geography': 'Iran', 'medium': 'Painting', 'site': 'Iran Darroudi official website', 'reference_label': 'Official artwork page', 'url': 'https://www.irandarroudi.com/en/paints', 'image': 'sources/rostan/growing-in-thus-way.jpg', 'credit': 'Artwork by Iran Darroudi', 'public_domain': False, 'rights': 'Copyright Iran Darroudi, all rights reserved. This low-resolution reference image is included for identification and commentary only', 'preserve_aspect': True}},
|
|
11
|
+
"Shahnameh": {'colors': ('#41356f', '#4b3d97', '#798cb8', '#6d866a', '#596956', '#d4b96b', '#d5c9b7', '#b0472c', '#a6373e'), 'order': (1, 8, 4, 7, 5, 6, 2, 3, 9), 'colorblind': False, 'persian': 'شاهنامه', 'pronunciation': 'shah-nah-MEH, Persian for Book of Kings', 'source': {'title': 'The Wedding of Siyavush and Farangis, Folio 185v from the Shahnama of Shah Tahmasp', 'citation': 'The Wedding of Siyavush and Farangis, folio 185v from the Shahnama of Shah Tahmasp, ca. 1525-30.', 'artist': "Painting attributed to Qasim ibn 'Ali, workshop directed by Mir Musavvir", 'date': 'ca. 1525-30', 'dimensions': 'painting 28.9 x 18.4 cm, page 47.3 x 32.1 cm', 'geography': 'Made in Iran, Tabriz', 'medium': 'Opaque watercolor, ink, silver and gold on paper', 'museum': 'The Metropolitan Museum of Art, New York', 'department': 'Islamic Art', 'accession': '1970.301.28', 'credit': 'Gift of Arthur A. Houghton Jr., 1970', 'url': 'https://www.metmuseum.org/art/collection/search/452137', 'image': 'sources/shahnameh/folio.jpg', 'download_url': 'https://images.metmuseum.org/CRDImages/is/original/DP107144.jpg', 'public_domain': True}},
|
|
12
|
+
"Gilas": {'colors': ('#58463f', '#598fb6', '#964765', '#b8715b', '#d8c723'), 'order': (1, 3, 5, 4, 2), 'colorblind': True, 'persian': 'گیلاس', 'pronunciation': 'gee-LAAS, Persian for cherry', 'source': {'title': 'Taste of Cherry promotional poster', 'note': 'Poster for the 1997 film directed by Abbas Kiarostami', 'citation': 'Taste of Cherry promotional poster, 1997.', 'date': '1997', 'geography': 'Iran', 'medium': 'Printed film poster', 'site': 'Wikipedia', 'reference_label': 'Poster file page', 'url': 'https://en.wikipedia.org/wiki/File:Tasteofcherryposter.jpg', 'image': 'sources/gilas/poster.jpg', 'credit': 'Promotional poster for Taste of Cherry', 'public_domain': False, 'rights': 'Copyrighted film poster, all rights reserved. Wikipedia uses its copy under a fair-use rationale that does not grant a reuse license. This 220-pixel reference is included for identification and commentary only', 'preserve_aspect': True}},
|
|
13
|
+
"Iwan": {'colors': ('#efbf15', '#b99241', '#6897b7', '#2e89ab', '#3263ac', '#0e37ae', '#0412cc', '#150c7d', '#06055a'), 'order': (1, 5, 3, 7, 4, 9, 6, 8, 2), 'colorblind': False, 'persian': 'ایوان', 'pronunciation': 'ee-VAHN, Persian for a vaulted hall open on one side', 'source': {'title': 'Entrance iwan of the Shah Mosque', 'citation': 'Entrance of the Shah Mosque of Isfahan, 2020.', 'artist': 'Farzan95, Farzan Dehbashi', 'date': '2020', 'geography': 'Isfahan, Iran', 'medium': 'Digital photograph', 'site': 'Wikimedia Commons', 'reference_label': 'Photograph and license record', 'url': 'https://commons.wikimedia.org/wiki/File:Shah_mosque_of_isfahan.jpg', 'image': 'sources/iwan/entrance.jpg', 'context_image': 'sources/iwan/iwan.jpg', 'context_caption': 'Looking up into the tiled entrance iwan of the Shah Mosque', 'context_url': 'https://commons.wikimedia.org/wiki/File:Mezquita_Shah,_Isfah%C3%A1n,_Ir%C3%A1n,_2016-09-20,_DD_64.jpg', 'context_credit': 'Photograph by Diego Delso, 2016', 'context_rights': 'Licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)', 'credit': 'Photograph by Farzan95, 2020', 'public_domain': False, 'rights': 'Licensed under [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/). Rang gallery images use resized adaptations', 'preserve_aspect': True}},
|
|
14
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: rang
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Color palettes from Persian art for plots and maps
|
|
5
|
+
Author: Mohsen Tahmasebi Nasab
|
|
6
|
+
Maintainer: Mohsen Tahmasebi Nasab
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
Project-URL: Homepage, https://github.com/mohsennasab/Rang
|
|
9
|
+
Project-URL: Source, https://github.com/mohsennasab/Rang
|
|
10
|
+
Project-URL: Documentation, https://github.com/mohsennasab/Rang#readme
|
|
11
|
+
Project-URL: Issues, https://github.com/mohsennasab/Rang/issues
|
|
12
|
+
Keywords: color,palette,colormap,matplotlib,persian art,visualization,cartography
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
24
|
+
Classifier: Topic :: Multimedia :: Graphics
|
|
25
|
+
Requires-Python: >=3.9
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
License-File: LICENSE
|
|
28
|
+
License-File: LICENSE-CC0.txt
|
|
29
|
+
Provides-Extra: plots
|
|
30
|
+
Requires-Dist: matplotlib>=3.7; extra == "plots"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# rang for Python
|
|
34
|
+
|
|
35
|
+
Color palettes from Persian art. Part of the [Rang](https://github.com/mohsennasab/Rang)
|
|
36
|
+
project, which also packages the palettes for R, ArcGIS Pro, QGIS, GeoLibre
|
|
37
|
+
and HEC-RAS.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
pip install rang
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
The package has no required dependencies. The matplotlib helpers need
|
|
44
|
+
matplotlib, which you can pull in at the same time:
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
pip install "rang[plots]"
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Colors
|
|
51
|
+
|
|
52
|
+
```python
|
|
53
|
+
import rang
|
|
54
|
+
|
|
55
|
+
rang.list_palettes() # available names
|
|
56
|
+
rang.rang("Kashan") # all nine colors, ramp order
|
|
57
|
+
rang.rang("Kashan", 4) # four well separated colors
|
|
58
|
+
rang.rang("Kashan", 30, "continuous") # interpolated ramp
|
|
59
|
+
rang.source("Kashan") # the artwork behind the palette
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Discrete requests use the stored separation order. Continuous requests
|
|
63
|
+
interpolate through the source ramp in sRGB.
|
|
64
|
+
|
|
65
|
+
## matplotlib
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
rang.cmap("Termeh") # smooth colormap
|
|
69
|
+
rang.cmap("Termeh", 6) # six fixed steps, for classified data
|
|
70
|
+
rang.cmap("Termeh", direction=-1) # reversed
|
|
71
|
+
|
|
72
|
+
rang.set_palette("Golestan") # default colors for lines and bars
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`register()` adds every palette to the matplotlib registry under a `rang:`
|
|
76
|
+
name. Do this once and any library that accepts a colormap name will take a
|
|
77
|
+
Rang palette, including xarray, geopandas, rioxarray and seaborn.
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
rang.register()
|
|
81
|
+
|
|
82
|
+
plt.imshow(data, cmap="rang:Iwan")
|
|
83
|
+
plt.imshow(data, cmap="rang:Iwan_r") # every palette also registers reversed
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Calling `register()` again is safe and quiet.
|
|
87
|
+
|
|
88
|
+
## Picking a palette
|
|
89
|
+
|
|
90
|
+
Termeh, Iwan, Khatam and Rostan run steadily from light to dark or dark to
|
|
91
|
+
light, which is what depth, elevation and rainfall maps need. Kashan,
|
|
92
|
+
Golestan and Mina move from a warm side through a light center to a cool
|
|
93
|
+
side. Nasir, Shahnameh and Gilas jump around in brightness, so they suit
|
|
94
|
+
categories rather than measured quantities.
|
|
95
|
+
|
|
96
|
+
Each palette page in the repository shows sample plots and the color vision
|
|
97
|
+
checks.
|
|
98
|
+
|
|
99
|
+
## License
|
|
100
|
+
|
|
101
|
+
The package software is available under the MIT License. To the extent that
|
|
102
|
+
copyright or database rights apply, the Rang palette definitions are
|
|
103
|
+
dedicated to the public domain under CC0 1.0 Universal. Photographs and
|
|
104
|
+
third-party data are covered by the notices in the main repository.
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
rang/__init__.py,sha256=kTt3kZ7fSX0a15kLTrMaCpp7lZp32XqC1D4lAUEp9rQ,6273
|
|
2
|
+
rang/_palettes.py,sha256=E7imRd7M9InV-eQctLTVP2-zhyGfthracodNtQJDwSQ,10466
|
|
3
|
+
rang-0.2.0.dist-info/licenses/LICENSE,sha256=VgPDfcrx_-uvFDwGNj8eBPJksKRC5m3JJqtFW3mn1yE,1079
|
|
4
|
+
rang-0.2.0.dist-info/licenses/LICENSE-CC0.txt,sha256=TVrtvpIdefraxaP34SEsKVpdBKSHKCP48pPXX_FfwW8,470
|
|
5
|
+
rang-0.2.0.dist-info/METADATA,sha256=1gwwtaYI3ZixaumX3y89VskuOSRcPsBUVUsB8BAPIFQ,3728
|
|
6
|
+
rang-0.2.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
7
|
+
rang-0.2.0.dist-info/top_level.txt,sha256=h9ZZxDnR_bq-sP2n-Hzusbh-dOVnkERdJDa0Qt1sm_A,5
|
|
8
|
+
rang-0.2.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Mohsen Tahmasebi Nasab
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
Rang palette data under CC0 1.0 Universal
|
|
2
|
+
|
|
3
|
+
To the extent possible under law, Mohsen Tahmasebi Nasab has waived all
|
|
4
|
+
copyright and related or neighboring rights in the Rang palette names, color
|
|
5
|
+
values, descriptions, and ordering.
|
|
6
|
+
|
|
7
|
+
This dedication applies only to the palette data. The Python software remains
|
|
8
|
+
licensed under the MIT License in LICENSE.
|
|
9
|
+
|
|
10
|
+
The complete CC0 1.0 Universal legal code is available at:
|
|
11
|
+
https://creativecommons.org/publicdomain/zero/1.0/legalcode
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
rang
|