Spaces:
Runtime error
Runtime error
Arnab Dey commited on
Commit ·
9902a03
1
Parent(s): d45952f
Add orientation feature to poster generation with validation
Browse files- app.py +27 -0
- create_map_poster.py +13 -1
app.py
CHANGED
|
@@ -45,8 +45,14 @@ class DistanceType(str, Enum):
|
|
| 45 |
NETWORK = "network"
|
| 46 |
|
| 47 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
NETWORK_TYPES = [item.value for item in NetworkType]
|
| 49 |
DIST_TYPES = [item.value for item in DistanceType]
|
|
|
|
| 50 |
|
| 51 |
|
| 52 |
_REPO_ROOT = Path(__file__).resolve().parent
|
|
@@ -60,6 +66,7 @@ class GenerateRequest(BaseModel):
|
|
| 60 |
dpi: int
|
| 61 |
network_type: NetworkType
|
| 62 |
dist_type: DistanceType
|
|
|
|
| 63 |
home_point: str | None = None
|
| 64 |
dot_size: float | None = None
|
| 65 |
|
|
@@ -111,6 +118,17 @@ class GenerateRequest(BaseModel):
|
|
| 111 |
except ValueError as exc:
|
| 112 |
raise ValueError("Invalid distance type.") from exc
|
| 113 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 114 |
@field_validator("dot_size", mode="before")
|
| 115 |
@classmethod
|
| 116 |
def _validate_dot_size(cls, value: str | float | None) -> float | None:
|
|
@@ -246,6 +264,7 @@ def generate(
|
|
| 246 |
dpi: int,
|
| 247 |
network_type: str,
|
| 248 |
dist_type: str,
|
|
|
|
| 249 |
home_point: str | None,
|
| 250 |
dot_size: float | None,
|
| 251 |
) -> str:
|
|
@@ -258,6 +277,7 @@ def generate(
|
|
| 258 |
dpi=dpi,
|
| 259 |
network_type=network_type,
|
| 260 |
dist_type=dist_type,
|
|
|
|
| 261 |
home_point=home_point,
|
| 262 |
dot_size=dot_size,
|
| 263 |
)
|
|
@@ -294,6 +314,7 @@ def generate(
|
|
| 294 |
dpi=request.dpi,
|
| 295 |
dot=dot_coords,
|
| 296 |
dot_size=request.dot_size if request.dot_size is not None else DEFAULT_DOT_SIZE,
|
|
|
|
| 297 |
)
|
| 298 |
return output_path
|
| 299 |
|
|
@@ -469,6 +490,11 @@ def build_demo() -> gr.Blocks:
|
|
| 469 |
choices=DIST_TYPES,
|
| 470 |
value="bbox",
|
| 471 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 472 |
home_point = gr.Textbox(
|
| 473 |
label="Home point (lat, lon)",
|
| 474 |
placeholder="31.3, 2.3",
|
|
@@ -500,6 +526,7 @@ def build_demo() -> gr.Blocks:
|
|
| 500 |
dpi,
|
| 501 |
network_type,
|
| 502 |
dist_type,
|
|
|
|
| 503 |
home_point,
|
| 504 |
dot_size,
|
| 505 |
],
|
|
|
|
| 45 |
NETWORK = "network"
|
| 46 |
|
| 47 |
|
| 48 |
+
class Orientation(str, Enum):
|
| 49 |
+
PORTRAIT = "portrait"
|
| 50 |
+
LANDSCAPE = "landscape"
|
| 51 |
+
|
| 52 |
+
|
| 53 |
NETWORK_TYPES = [item.value for item in NetworkType]
|
| 54 |
DIST_TYPES = [item.value for item in DistanceType]
|
| 55 |
+
ORIENTATION_TYPES = [item.value for item in Orientation]
|
| 56 |
|
| 57 |
|
| 58 |
_REPO_ROOT = Path(__file__).resolve().parent
|
|
|
|
| 66 |
dpi: int
|
| 67 |
network_type: NetworkType
|
| 68 |
dist_type: DistanceType
|
| 69 |
+
orientation: Orientation
|
| 70 |
home_point: str | None = None
|
| 71 |
dot_size: float | None = None
|
| 72 |
|
|
|
|
| 118 |
except ValueError as exc:
|
| 119 |
raise ValueError("Invalid distance type.") from exc
|
| 120 |
|
| 121 |
+
@field_validator("orientation", mode="before")
|
| 122 |
+
@classmethod
|
| 123 |
+
def _validate_orientation(cls, value: str | Orientation) -> Orientation:
|
| 124 |
+
if isinstance(value, Orientation):
|
| 125 |
+
return value
|
| 126 |
+
value = (value or "").strip()
|
| 127 |
+
try:
|
| 128 |
+
return Orientation(value)
|
| 129 |
+
except ValueError as exc:
|
| 130 |
+
raise ValueError("Invalid orientation.") from exc
|
| 131 |
+
|
| 132 |
@field_validator("dot_size", mode="before")
|
| 133 |
@classmethod
|
| 134 |
def _validate_dot_size(cls, value: str | float | None) -> float | None:
|
|
|
|
| 264 |
dpi: int,
|
| 265 |
network_type: str,
|
| 266 |
dist_type: str,
|
| 267 |
+
orientation: str,
|
| 268 |
home_point: str | None,
|
| 269 |
dot_size: float | None,
|
| 270 |
) -> str:
|
|
|
|
| 277 |
dpi=dpi,
|
| 278 |
network_type=network_type,
|
| 279 |
dist_type=dist_type,
|
| 280 |
+
orientation=orientation,
|
| 281 |
home_point=home_point,
|
| 282 |
dot_size=dot_size,
|
| 283 |
)
|
|
|
|
| 314 |
dpi=request.dpi,
|
| 315 |
dot=dot_coords,
|
| 316 |
dot_size=request.dot_size if request.dot_size is not None else DEFAULT_DOT_SIZE,
|
| 317 |
+
orientation=request.orientation.value,
|
| 318 |
)
|
| 319 |
return output_path
|
| 320 |
|
|
|
|
| 490 |
choices=DIST_TYPES,
|
| 491 |
value="bbox",
|
| 492 |
)
|
| 493 |
+
orientation = gr.Dropdown(
|
| 494 |
+
label="Orientation",
|
| 495 |
+
choices=ORIENTATION_TYPES,
|
| 496 |
+
value="portrait",
|
| 497 |
+
)
|
| 498 |
home_point = gr.Textbox(
|
| 499 |
label="Home point (lat, lon)",
|
| 500 |
placeholder="31.3, 2.3",
|
|
|
|
| 526 |
dpi,
|
| 527 |
network_type,
|
| 528 |
dist_type,
|
| 529 |
+
orientation,
|
| 530 |
home_point,
|
| 531 |
dot_size,
|
| 532 |
],
|
create_map_poster.py
CHANGED
|
@@ -359,6 +359,15 @@ def _coerce_coordinates(point: Coordinates | Sequence[float]) -> Coordinates:
|
|
| 359 |
raise TypeError("point must be Coordinates or (lat, lon) sequence")
|
| 360 |
|
| 361 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 362 |
def create_poster(
|
| 363 |
city: str,
|
| 364 |
country: str,
|
|
@@ -371,6 +380,7 @@ def create_poster(
|
|
| 371 |
dpi: int = 300,
|
| 372 |
dot: Coordinates | Sequence[float] | None = None,
|
| 373 |
dot_size: float = 60,
|
|
|
|
| 374 |
) -> None:
|
| 375 |
print(f"\nGenerating map for {city}, {country}...")
|
| 376 |
theme = _require_theme()
|
|
@@ -410,7 +420,9 @@ def create_poster(
|
|
| 410 |
|
| 411 |
# 2. Setup Plot
|
| 412 |
print("Rendering map...")
|
| 413 |
-
|
|
|
|
|
|
|
| 414 |
ax.set_facecolor(theme.bg)
|
| 415 |
ax.set_position([0, 0, 1, 1])
|
| 416 |
|
|
|
|
| 359 |
raise TypeError("point must be Coordinates or (lat, lon) sequence")
|
| 360 |
|
| 361 |
|
| 362 |
+
def _normalize_orientation(value: str | None) -> str:
|
| 363 |
+
if not value:
|
| 364 |
+
return "portrait"
|
| 365 |
+
cleaned = str(value).strip().lower()
|
| 366 |
+
if cleaned in {"portrait", "landscape"}:
|
| 367 |
+
return cleaned
|
| 368 |
+
raise ValueError("Orientation must be 'portrait' or 'landscape'.")
|
| 369 |
+
|
| 370 |
+
|
| 371 |
def create_poster(
|
| 372 |
city: str,
|
| 373 |
country: str,
|
|
|
|
| 380 |
dpi: int = 300,
|
| 381 |
dot: Coordinates | Sequence[float] | None = None,
|
| 382 |
dot_size: float = 60,
|
| 383 |
+
orientation: str = "portrait",
|
| 384 |
) -> None:
|
| 385 |
print(f"\nGenerating map for {city}, {country}...")
|
| 386 |
theme = _require_theme()
|
|
|
|
| 420 |
|
| 421 |
# 2. Setup Plot
|
| 422 |
print("Rendering map...")
|
| 423 |
+
orientation = _normalize_orientation(orientation)
|
| 424 |
+
figsize = (12, 16) if orientation == "portrait" else (16, 12)
|
| 425 |
+
fig, ax = plt.subplots(figsize=figsize, facecolor=theme.bg)
|
| 426 |
ax.set_facecolor(theme.bg)
|
| 427 |
ax.set_position([0, 0, 1, 1])
|
| 428 |
|