Добавить PrettyMaps/generate.py

This commit is contained in:
m9k
2026-09-27 13:42:13 +00:00
parent 212870fd33
commit a8d511f046
+219
View File
@@ -0,0 +1,219 @@
#!/usr/bin/env python3
"""
Generate a prettymaps poster.
Parameters (priority: CLI args > env vars > defaults):
QUERY / --query location string (geocoded by osmnx)
LAT / --lat latitude (use with LON instead of QUERY)
LON / --lon longitude
RADIUS_M / --radius radius in metres
CIRCLE / --circle circular clip (true/false, 1/0, yes/no)
TITLE / --title map title
PRESET / --preset prettymaps preset name
OUTPUT_NAME / --output-name basename for png/pdf (no extension)
DPI / --dpi output dpi
"""
import argparse
import os
from pathlib import Path
import osmnx as ox
# osmnx 2.x: project_gdf left the top-level namespace
if not hasattr(ox, "project_gdf"):
ox.project_gdf = ox.projection.project_gdf
if not hasattr(ox, "project_graph"):
if hasattr(ox, "projection") and hasattr(ox.projection, "project_graph"):
ox.project_graph = ox.projection.project_graph
import prettymaps
import prettymaps.draw as _pm_draw
import matplotlib
matplotlib.use("Agg")
# --- patch: KeyError 'highway' when streets gdf has no highway column ---
_orig_graph_to_shapely = _pm_draw.graph_to_shapely
def _safe_graph_to_shapely(gdf, width=1.0):
if gdf is None or getattr(gdf, "empty", True):
from shapely.geometry import GeometryCollection
return GeometryCollection()
if isinstance(width, dict) and "highway" not in gdf.columns:
width = width.get("default", next(iter(width.values()), 1.0))
return _orig_graph_to_shapely(gdf, width)
_pm_draw.graph_to_shapely = _safe_graph_to_shapely
# -----------------------------------------------------------------------
# --- defaults (single place) ---
DEFAULT_QUERY = "Akademgorodok, Novosibirsk, Russia"
DEFAULT_LAT = None
DEFAULT_LON = None
DEFAULT_RADIUS_M = 2200
DEFAULT_CIRCLE = True
DEFAULT_TITLE = "Академгородок · Новосибирск"
DEFAULT_PRESET = "default"
DEFAULT_OUTPUT_NAME = "akademgorodok_poster"
DEFAULT_DPI = 300
DEFAULT_FIGSIZE = (14, 14)
# --------------------------------
def _env(name, default=None):
val = os.environ.get(name)
if val is None or val == "":
return default
return val
def _env_bool(name, default):
val = os.environ.get(name)
if val is None or val == "":
return default
return val.strip().lower() in ("1", "true", "yes", "y", "on")
def _env_float(name, default):
val = os.environ.get(name)
if val is None or val == "":
return default
return float(val)
def _env_int(name, default):
val = os.environ.get(name)
if val is None or val == "":
return default
return int(val)
def parse_args():
p = argparse.ArgumentParser(description="Generate a prettymaps poster")
p.add_argument(
"--query",
default=_env("QUERY", DEFAULT_QUERY),
help="Location string for geocoding",
)
p.add_argument(
"--lat",
type=float,
default=_env_float("LAT", DEFAULT_LAT),
help="Latitude (use with --lon instead of --query)",
)
p.add_argument(
"--lon",
type=float,
default=_env_float("LON", DEFAULT_LON),
help="Longitude (use with --lat instead of --query)",
)
p.add_argument(
"--radius",
type=int,
default=_env_int("RADIUS_M", DEFAULT_RADIUS_M),
help="Radius in metres",
)
p.add_argument(
"--circle",
default=None,
help="Circular clip: true/false (env CIRCLE)",
)
p.add_argument(
"--title",
default=_env("TITLE", DEFAULT_TITLE),
help="Map title",
)
p.add_argument(
"--preset",
default=_env("PRESET", DEFAULT_PRESET),
help="prettymaps preset name",
)
p.add_argument(
"--output-name",
default=_env("OUTPUT_NAME", DEFAULT_OUTPUT_NAME),
help="Output basename without extension",
)
p.add_argument(
"--dpi",
type=int,
default=_env_int("DPI", DEFAULT_DPI),
help="Output DPI",
)
args = p.parse_args()
if args.circle is None:
args.circle = _env_bool("CIRCLE", DEFAULT_CIRCLE)
else:
args.circle = str(args.circle).strip().lower() in (
"1",
"true",
"yes",
"y",
"on",
)
return args
def main():
args = parse_args()
output_dir = Path("/work") if Path("/work").is_dir() else Path(".")
output_dir.mkdir(parents=True, exist_ok=True)
out_png = output_dir / f"{args.output_name}.png"
out_pdf = output_dir / f"{args.output_name}.pdf"
if args.lat is not None and args.lon is not None:
location = (args.lat, args.lon)
location_desc = f"({args.lat}, {args.lon})"
else:
location = args.query
location_desc = args.query
print(f"Location: {location_desc}")
print(f"Radius: {args.radius} m, circle={args.circle}, preset={args.preset}")
print(f"Title: {args.title}")
print("Downloading OSM data and rendering (this can take several minutes)...")
plot = prettymaps.plot(
location,
radius=args.radius,
circle=args.circle,
figsize=DEFAULT_FIGSIZE,
preset=args.preset,
)
if args.title:
plot.ax.set_title(
args.title,
fontsize=28,
pad=20,
fontfamily="DejaVu Sans",
)
plot.fig.savefig(
out_png,
dpi=args.dpi,
bbox_inches="tight",
facecolor=plot.fig.get_facecolor(),
edgecolor="none",
)
print(f"Saved: {out_png}")
plot.fig.savefig(
out_pdf,
bbox_inches="tight",
facecolor=plot.fig.get_facecolor(),
edgecolor="none",
)
print(f"Saved: {out_pdf}")
print("Done.")
if __name__ == "__main__":
main()