Arnab Dey commited on
Commit
9902a03
·
1 Parent(s): d45952f

Add orientation feature to poster generation with validation

Browse files
Files changed (2) hide show
  1. app.py +27 -0
  2. 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
- fig, ax = plt.subplots(figsize=(12, 16), facecolor=theme.bg)
 
 
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