Spaces:
Running
Running
Tune gradient fade thickness by format and orientation
Browse files- create_map_poster.py +65 -22
create_map_poster.py
CHANGED
|
@@ -165,13 +165,15 @@ def crop_axes_to_ratio(ax, target_ratio, pad=0.03):
|
|
| 165 |
ax.set_ylim(cy - new_h / 2, cy + new_h / 2)
|
| 166 |
|
| 167 |
|
| 168 |
-
def create_gradient_fade(ax, color, location='bottom', zorder=10):
|
| 169 |
"""
|
| 170 |
-
Creates a fade effect at the
|
| 171 |
-
|
|
|
|
| 172 |
"""
|
|
|
|
|
|
|
| 173 |
vals = np.linspace(0, 1, 256).reshape(-1, 1)
|
| 174 |
-
gradient = np.hstack((vals, vals))
|
| 175 |
|
| 176 |
rgb = mcolors.to_rgb(color)
|
| 177 |
my_colors = np.zeros((256, 4))
|
|
@@ -179,29 +181,50 @@ def create_gradient_fade(ax, color, location='bottom', zorder=10):
|
|
| 179 |
my_colors[:, 1] = rgb[1]
|
| 180 |
my_colors[:, 2] = rgb[2]
|
| 181 |
|
| 182 |
-
if location == 'bottom':
|
| 183 |
-
my_colors[:, 3] = np.linspace(1, 0, 256)
|
| 184 |
-
extent_y_start = 0
|
| 185 |
-
extent_y_end = 0.25
|
| 186 |
-
else:
|
| 187 |
-
my_colors[:, 3] = np.linspace(0, 1, 256)
|
| 188 |
-
extent_y_start = 0.75
|
| 189 |
-
extent_y_end = 1.0
|
| 190 |
-
|
| 191 |
-
custom_cmap = mcolors.ListedColormap(my_colors)
|
| 192 |
-
|
| 193 |
# Save current view/aspect so imshow can't distort the map
|
| 194 |
xlim = ax.get_xlim()
|
| 195 |
ylim = ax.get_ylim()
|
| 196 |
aspect = ax.get_aspect()
|
| 197 |
|
|
|
|
| 198 |
y_range = ylim[1] - ylim[0]
|
| 199 |
-
|
| 200 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 201 |
|
| 202 |
ax.imshow(
|
| 203 |
gradient,
|
| 204 |
-
extent=
|
| 205 |
aspect='auto',
|
| 206 |
cmap=custom_cmap,
|
| 207 |
zorder=zorder,
|
|
@@ -420,6 +443,22 @@ def create_poster(city, country, point, dist, output_file):
|
|
| 420 |
orientation = THEME.get("_orientation", "portrait")
|
| 421 |
figsize = get_figsize(map_format, orientation)
|
| 422 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 423 |
fig = plt.figure(figsize=figsize, facecolor=THEME['bg'])
|
| 424 |
|
| 425 |
# Map axis: full-bleed
|
|
@@ -451,12 +490,16 @@ def create_poster(city, country, point, dist, output_file):
|
|
| 451 |
)
|
| 452 |
|
| 453 |
# Crop view to match output aspect ratio
|
| 454 |
-
target_ratio = figsize[0] / figsize[1]
|
| 455 |
crop_axes_to_ratio(ax_map, target_ratio=target_ratio, pad=MAP_PAD)
|
| 456 |
|
| 457 |
-
# Layer 3: gradients
|
| 458 |
-
create_gradient_fade(ax_map, THEME['gradient_color'], location='bottom', zorder=10)
|
| 459 |
-
create_gradient_fade(ax_map, THEME['gradient_color'], location='top', zorder=10)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 460 |
|
| 461 |
# Build displayed strings
|
| 462 |
spaced_city = " ".join(list(city.upper()))
|
|
|
|
| 165 |
ax.set_ylim(cy - new_h / 2, cy + new_h / 2)
|
| 166 |
|
| 167 |
|
| 168 |
+
def create_gradient_fade(ax, color, location='bottom', zorder=10, thickness=0.25):
|
| 169 |
"""
|
| 170 |
+
Creates a fade effect at the edges of the map without altering aspect/limits.
|
| 171 |
+
location: 'bottom', 'top', 'left', 'right'
|
| 172 |
+
thickness: fraction of the axis range to cover (e.g. 0.25 = 25%)
|
| 173 |
"""
|
| 174 |
+
thickness = float(max(0.02, min(0.40, thickness))) # clamp safe
|
| 175 |
+
|
| 176 |
vals = np.linspace(0, 1, 256).reshape(-1, 1)
|
|
|
|
| 177 |
|
| 178 |
rgb = mcolors.to_rgb(color)
|
| 179 |
my_colors = np.zeros((256, 4))
|
|
|
|
| 181 |
my_colors[:, 1] = rgb[1]
|
| 182 |
my_colors[:, 2] = rgb[2]
|
| 183 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 184 |
# Save current view/aspect so imshow can't distort the map
|
| 185 |
xlim = ax.get_xlim()
|
| 186 |
ylim = ax.get_ylim()
|
| 187 |
aspect = ax.get_aspect()
|
| 188 |
|
| 189 |
+
x_range = xlim[1] - xlim[0]
|
| 190 |
y_range = ylim[1] - ylim[0]
|
| 191 |
+
|
| 192 |
+
if location == 'bottom':
|
| 193 |
+
gradient = np.hstack((vals, vals)) # vertical
|
| 194 |
+
my_colors[:, 3] = np.linspace(1, 0, 256)
|
| 195 |
+
y0 = ylim[0]
|
| 196 |
+
y1 = ylim[0] + y_range * thickness
|
| 197 |
+
extent = [xlim[0], xlim[1], y0, y1]
|
| 198 |
+
|
| 199 |
+
elif location == 'top':
|
| 200 |
+
gradient = np.hstack((vals, vals)) # vertical
|
| 201 |
+
my_colors[:, 3] = np.linspace(0, 1, 256)
|
| 202 |
+
y0 = ylim[1] - y_range * thickness
|
| 203 |
+
y1 = ylim[1]
|
| 204 |
+
extent = [xlim[0], xlim[1], y0, y1]
|
| 205 |
+
|
| 206 |
+
elif location == 'left':
|
| 207 |
+
gradient = np.vstack((vals.T, vals.T)) # horizontal
|
| 208 |
+
my_colors[:, 3] = np.linspace(1, 0, 256)
|
| 209 |
+
x0 = xlim[0]
|
| 210 |
+
x1 = xlim[0] + x_range * thickness
|
| 211 |
+
extent = [x0, x1, ylim[0], ylim[1]]
|
| 212 |
+
|
| 213 |
+
elif location == 'right':
|
| 214 |
+
gradient = np.vstack((vals.T, vals.T)) # horizontal
|
| 215 |
+
my_colors[:, 3] = np.linspace(0, 1, 256)
|
| 216 |
+
x0 = xlim[1] - x_range * thickness
|
| 217 |
+
x1 = xlim[1]
|
| 218 |
+
extent = [x0, x1, ylim[0], ylim[1]]
|
| 219 |
+
|
| 220 |
+
else:
|
| 221 |
+
return
|
| 222 |
+
|
| 223 |
+
custom_cmap = mcolors.ListedColormap(my_colors)
|
| 224 |
|
| 225 |
ax.imshow(
|
| 226 |
gradient,
|
| 227 |
+
extent=extent,
|
| 228 |
aspect='auto',
|
| 229 |
cmap=custom_cmap,
|
| 230 |
zorder=zorder,
|
|
|
|
| 443 |
orientation = THEME.get("_orientation", "portrait")
|
| 444 |
figsize = get_figsize(map_format, orientation)
|
| 445 |
|
| 446 |
+
target_ratio = figsize[0] / figsize[1] # width/height
|
| 447 |
+
|
| 448 |
+
# Tune fade thickness by format/orientation (carto-friendly defaults)
|
| 449 |
+
if map_format == "panoramic" and orientation == "landscape":
|
| 450 |
+
fade_tb = 0.12
|
| 451 |
+
fade_lr = 0.22
|
| 452 |
+
elif orientation == "landscape":
|
| 453 |
+
fade_tb = 0.15
|
| 454 |
+
fade_lr = 0.18
|
| 455 |
+
elif map_format == "plan":
|
| 456 |
+
fade_tb = 0.18
|
| 457 |
+
fade_lr = 0.18
|
| 458 |
+
else: # portrait (default)
|
| 459 |
+
fade_tb = 0.22
|
| 460 |
+
fade_lr = 0.10
|
| 461 |
+
|
| 462 |
fig = plt.figure(figsize=figsize, facecolor=THEME['bg'])
|
| 463 |
|
| 464 |
# Map axis: full-bleed
|
|
|
|
| 490 |
)
|
| 491 |
|
| 492 |
# Crop view to match output aspect ratio
|
|
|
|
| 493 |
crop_axes_to_ratio(ax_map, target_ratio=target_ratio, pad=MAP_PAD)
|
| 494 |
|
| 495 |
+
# Layer 3: gradients (thickness adapts to layout)
|
| 496 |
+
create_gradient_fade(ax_map, THEME['gradient_color'], location='bottom', zorder=10, thickness=fade_tb)
|
| 497 |
+
create_gradient_fade(ax_map, THEME['gradient_color'], location='top', zorder=10, thickness=fade_tb)
|
| 498 |
+
|
| 499 |
+
# Add side fades for wide layouts; thinner in portrait by design
|
| 500 |
+
if target_ratio >= 1.15:
|
| 501 |
+
create_gradient_fade(ax_map, THEME['gradient_color'], location='left', zorder=10, thickness=fade_lr)
|
| 502 |
+
create_gradient_fade(ax_map, THEME['gradient_color'], location='right', zorder=10, thickness=fade_lr)
|
| 503 |
|
| 504 |
# Build displayed strings
|
| 505 |
spaced_city = " ".join(list(city.upper()))
|