diff --git a/README.md b/README.md index 59d32aa8b..9dbc29dbf 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,9 @@ python create_map_poster.py --city --country [options] | **OPTIONAL:** `--all-themes` | | Generate posters for all available themes | | | **OPTIONAL:** `--width` | `-W` | Image width in inches | 12 (max: 20) | | **OPTIONAL:** `--height` | `-H` | Image height in inches | 16 (max: 20) | +| **OPTIONAL:** `--output` | `-o` | Path to output directory | posters | +| **OPTIONAL:** `--points-of-interest-key` | `-poiK` | Top level tag from OpenStreetMap ( use with --points-of-interest-val) | | +| **OPTIONAL:** `--points-of-interest-val` | `-poiV` | List of points of interest under the top level tag ( use with --points-of-interest-key) | | ### Multilingual Support - i18n diff --git a/create_map_poster.py b/create_map_poster.py index 3eab412b2..7fa1cda2d 100755 --- a/create_map_poster.py +++ b/create_map_poster.py @@ -20,6 +20,7 @@ import matplotlib.colors as mcolors import matplotlib.pyplot as plt +from matplotlib.patches import Circle import numpy as np import osmnx as ox from geopandas import GeoDataFrame @@ -492,6 +493,7 @@ def create_poster( name_label=None, display_city=None, display_country=None, + poi_dict={}, fonts=None, ): """ @@ -524,7 +526,7 @@ def create_poster( # Progress bar for data fetching with tqdm( - total=3, + total=4, desc="Fetching map data", unit="step", bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt}", @@ -557,6 +559,17 @@ def create_poster( ) pbar.update(1) + # 4. Fetch POIs + if len(poi_dict) > 0 and poi_dict[next(iter(poi_dict))] != []: + pbar.set_description("Downloading points of interest") + points_of_interest = fetch_features( + point, + compensated_dist, + tags=poi_dict, + name="_".join(poi_dict[next(iter(poi_dict))]) + ) + pbar.update(1) + print("✓ All data retrieved successfully!") # 2. Setup Plot @@ -591,13 +604,20 @@ def create_poster( except Exception: parks_polys = parks_polys.to_crs(g_proj.graph['crs']) parks_polys.plot(ax=ax, facecolor=THEME['parks'], edgecolor='none', zorder=0.8) + # Layer 2: Roads with hierarchy coloring print("Applying road hierarchy colors...") edge_colors = get_edge_colors_by_type(g_proj) edge_widths = get_edge_widths_by_type(g_proj) + # Determine cropping limits to maintain the poster aspect ratio crop_xlim, crop_ylim = get_crop_limits(g_proj, point, fig, compensated_dist) + + # Calculate scale factor based on smaller dimension (reference 12 inches) + # This ensures text scales properly for both portrait and landscape orientations + scale_factor = min(height, width) / 12.0 + # Plot the projected graph and then apply the cropped limits ox.plot_graph( g_proj, ax=ax, bgcolor=THEME['bg'], @@ -611,14 +631,32 @@ def create_poster( ax.set_xlim(crop_xlim) ax.set_ylim(crop_ylim) - # Layer 3: Gradients (Top and Bottom) + # Layer 3: Points of interest + if (len(poi_dict) > 0 and poi_dict[next(iter(poi_dict))] != []) \ + and points_of_interest is not None and not points_of_interest.empty: + points_of_interest = ox.projection.project_gdf(points_of_interest) + for poi in points_of_interest.geometry: + center = (0,0) + if poi.geom_type == "Point": + center = poi.x, poi.y + else: + center = poi.centroid.x, poi.centroid.y + + c = Circle( + center, + radius=12 * scale_factor, + facecolor=THEME.get("poi", "text"), + edgecolor=THEME.get("poi", "text"), + linewidth=1, + alpha=1, + zorder=9, + ) + ax.add_patch(c) + + # Layer 4: Gradients (Top and Bottom) create_gradient_fade(ax, THEME['gradient_color'], location='bottom', zorder=10) create_gradient_fade(ax, THEME['gradient_color'], location='top', zorder=10) - # Calculate scale factor based on smaller dimension (reference 12 inches) - # This ensures text scales properly for both portrait and landscape orientations - scale_factor = min(height, width) / 12.0 - # Base font sizes (at 12 inches width) base_main = 60 base_sub = 22 @@ -680,6 +718,7 @@ def create_poster( family="monospace", weight="bold", size=adjusted_font_size ) + # --- BOTTOM TEXT --- ax.text( 0.5, @@ -955,6 +994,28 @@ def list_themes(): choices=["png", "svg", "pdf"], help="Output format for the poster (default: png)", ) + parser.add_argument( + "--output", + "-o", + type=str, + default="posters", + help="Path to output directory for the poster (default: posters)" + ) + parser.add_argument( + "--points-of-interest-key", + "-poiK", + type=str, + default="", + help="Top level tag from OpenStreetMap (ex: amenities)" + ) + parser.add_argument( + "--points-of-interest-val", + "-poiV", + type=str, + nargs='+', + default=[], + help="List of points of interest under the top level tag (ex: pub)" + ) args = parser.parse_args() @@ -986,6 +1047,10 @@ def list_themes(): ) args.height = 20.0 + # Change output directory + if args.output: + POSTERS_DIR = args.output + available_themes = get_available_themes() if not available_themes: print("No themes found in 'themes/' directory.") @@ -1036,6 +1101,7 @@ def list_themes(): country_label=args.country_label, display_city=args.display_city, display_country=args.display_country, + poi_dict={ args.points_of_interest_key: args.points_of_interest_val }, fonts=custom_fonts, ) diff --git a/themes/terracotta.json b/themes/terracotta.json index 086740fb9..05d3b898c 100644 --- a/themes/terracotta.json +++ b/themes/terracotta.json @@ -11,5 +11,6 @@ "road_secondary": "#C9846A", "road_tertiary": "#D9A08A", "road_residential": "#E5C4B0", - "road_default": "#D9A08A" + "road_default": "#D9A08A", + "poi": "#F75E00" }