fffiloni commited on
Commit
a25a161
·
verified ·
1 Parent(s): 4727c3a

preparing UI for major features upgrade

Browse files
Files changed (1) hide show
  1. app.py +162 -38
app.py CHANGED
@@ -8,7 +8,6 @@ POSTERS_DIR = ROOT / "posters"
8
 
9
  # ---- UI CSS (mobile/tablet friendly width) ----
10
  APP_CSS = """
11
- /* Center the app and limit its width (iPhone + iPad friendly) */
12
  .gradio-container {
13
  max-width: 768px !important;
14
  margin: 0 auto !important;
@@ -17,6 +16,17 @@ APP_CSS = """
17
  }
18
  """
19
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  def list_themes():
22
  if THEMES_DIR.exists():
@@ -44,14 +54,90 @@ def latest_new_png(before_paths):
44
 
45
 
46
  def preset_to_distance(preset):
47
- return {
48
- "Small town": 3000,
49
- "Town": 5000,
50
- "City": 8000,
51
- "Big city": 10000,
52
- "Major metro": 12000,
53
- "Custom": None,
54
- }.get(preset, None)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
 
56
 
57
  def generate(
@@ -66,6 +152,8 @@ def generate(
66
  fast_mode,
67
  map_format,
68
  orientation,
 
 
69
  ):
70
  """
71
  Run the poster script and stream logs into the UI.
@@ -81,7 +169,7 @@ def generate(
81
  if fast_mode:
82
  distance_m = min(int(distance_m), 12000)
83
 
84
- # If format is Plan, orientation is ignored by the script, but keep it stable
85
  if map_format == "plan":
86
  orientation = "portrait"
87
 
@@ -93,15 +181,23 @@ def generate(
93
  "--orientation", orientation,
94
  ]
95
 
 
 
 
 
 
 
 
 
96
  # Center selection
97
  if center_mode == "Coordinates":
98
- # Basic validation to avoid cryptic script errors
99
  if lat is None or lon is None:
100
  yield "❌ Please provide both latitude and longitude.", None
101
  return
 
102
  cmd += ["--lat", str(float(lat)), "--lon", str(float(lon))]
103
 
104
- # City/Country become labels (optional but helpful)
105
  if city:
106
  cmd += ["--city", city]
107
  if country:
@@ -109,10 +205,10 @@ def generate(
109
 
110
  cmd_note = f"Center: Coordinates (lat={lat}, lon={lon})"
111
  else:
112
- # City mode requires city/country
113
  if not city or not country:
114
  yield "❌ Please provide both City and Country (or switch to Coordinates).", None
115
  return
 
116
  cmd += ["--city", city, "--country", country]
117
  cmd_note = f"Center: City ({city}, {country})"
118
 
@@ -122,7 +218,7 @@ def generate(
122
  status_lines.append("🚀 Starting… (OSM downloads can be slow on shared Spaces)")
123
  status_lines.append(cmd_note)
124
  status_lines.append(f"Command: {' '.join(cmd)}")
125
- status_lines.append("Tip: if you see '0%|0/3' for a while, it may jump to 33% all at once.")
126
  yield "\n".join(status_lines), None
127
 
128
  proc = subprocess.Popen(
@@ -139,10 +235,8 @@ def generate(
139
  if not line:
140
  continue
141
  status_lines.append(line)
142
-
143
  if len(status_lines) > 250:
144
  status_lines = status_lines[-250:]
145
-
146
  yield "\n".join(status_lines), None
147
  finally:
148
  rc = proc.wait()
@@ -163,7 +257,7 @@ def generate(
163
 
164
 
165
  def on_format_change(fmt):
166
- """Disable orientation control when Plan is selected (Plan is always square)."""
167
  if fmt == "plan":
168
  return gr.update(value="portrait", interactive=False)
169
  return gr.update(interactive=True)
@@ -173,12 +267,11 @@ def on_center_mode_change(mode):
173
  """Show lat/lon inputs only when Coordinates is selected."""
174
  if mode == "Coordinates":
175
  return (
176
- gr.update(visible=True), # city label still useful
177
  gr.update(visible=True),
178
  gr.update(visible=True),
179
  gr.update(visible=True),
180
  )
181
- # City mode
182
  return (
183
  gr.update(visible=True),
184
  gr.update(visible=True),
@@ -188,16 +281,7 @@ def on_center_mode_change(mode):
188
 
189
 
190
  with gr.Blocks(css=APP_CSS) as demo:
191
-
192
- gr.Markdown(
193
- """
194
- # MapToPoster – Gradio
195
-
196
- Original project by Ankur (MapToPoster)
197
- Based on the open-source repository: https://github.com/originalankur/maptoposter
198
- Many thanks for the original work and inspiration.
199
- """
200
- )
201
 
202
  center_mode = gr.Radio(
203
  label="Center",
@@ -217,14 +301,14 @@ with gr.Blocks(css=APP_CSS) as demo:
217
  on_center_mode_change,
218
  inputs=[center_mode],
219
  outputs=[city, country, lat, lon],
 
220
  )
221
 
222
- with gr.Row():
223
- theme = gr.Dropdown(
224
- label="Theme",
225
- choices=THEMES,
226
- value=(THEMES[0] if THEMES else None),
227
- )
228
 
229
  with gr.Row():
230
  preset = gr.Dropdown(
@@ -237,9 +321,37 @@ with gr.Blocks(css=APP_CSS) as demo:
237
  minimum=1000,
238
  maximum=20000,
239
  step=250,
240
- value=2000,
241
  )
242
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
243
  with gr.Row():
244
  map_format = gr.Dropdown(
245
  label="Format",
@@ -252,7 +364,12 @@ with gr.Blocks(css=APP_CSS) as demo:
252
  value="portrait",
253
  )
254
 
255
- map_format.change(on_format_change, inputs=[map_format], outputs=[orientation])
 
 
 
 
 
256
 
257
  fast_mode = gr.Checkbox(label="Fast mode (cap ~12km)", value=True)
258
 
@@ -262,8 +379,15 @@ with gr.Blocks(css=APP_CSS) as demo:
262
 
263
  btn.click(
264
  generate,
265
- inputs=[center_mode, city, country, lat, lon, theme, preset, distance, fast_mode, map_format, orientation],
 
 
 
 
 
 
266
  outputs=[status, out],
 
267
  )
268
 
269
  demo.launch()
 
8
 
9
  # ---- UI CSS (mobile/tablet friendly width) ----
10
  APP_CSS = """
 
11
  .gradio-container {
12
  max-width: 768px !important;
13
  margin: 0 auto !important;
 
16
  }
17
  """
18
 
19
+ # Preset distances (meters)
20
+ PRESET_DIST = {
21
+ "Small town": 3000,
22
+ "Town": 5000,
23
+ "City": 8000,
24
+ "Big city": 10000,
25
+ "Major metro": 12000,
26
+ "Custom": None,
27
+ }
28
+ TOWN_DIST = PRESET_DIST["Town"] # threshold for enabling buildings in Custom mode
29
+
30
 
31
  def list_themes():
32
  if THEMES_DIR.exists():
 
54
 
55
 
56
  def preset_to_distance(preset):
57
+ return PRESET_DIST.get(preset, None)
58
+
59
+
60
+ def distance_to_preset(distance_m: int):
61
+ """If distance matches a preset exactly, return that preset name; else Custom."""
62
+ for name, val in PRESET_DIST.items():
63
+ if name == "Custom":
64
+ continue
65
+ if val is not None and int(distance_m) == int(val):
66
+ return name
67
+ return "Custom"
68
+
69
+
70
+ def buildings_allowed(preset: str, distance_m: int):
71
+ """
72
+ Buildings can be enabled only when:
73
+ - preset is Small town or Town
74
+ - OR preset is Custom and distance <= Town distance
75
+ """
76
+ if preset in ("Small town", "Town"):
77
+ return True
78
+ if preset == "Custom" and int(distance_m) <= int(TOWN_DIST):
79
+ return True
80
+ return False
81
+
82
+
83
+ def format_distance_hint(preset: str, distance_m: int):
84
+ preset_line = (
85
+ f"**Preset guide** — Small town: {PRESET_DIST['Small town']}m · "
86
+ f"Town: {PRESET_DIST['Town']}m · City: {PRESET_DIST['City']}m · "
87
+ f"Big city: {PRESET_DIST['Big city']}m · Major metro: {PRESET_DIST['Major metro']}m"
88
+ )
89
+ current = f"**Current** — Preset: `{preset}` · Distance: `{int(distance_m)}m`"
90
+ buildings_note = (
91
+ f"**Buildings layer** is available for `Small town`/`Town`, "
92
+ f"or `Custom` when distance ≤ `{TOWN_DIST}m`."
93
+ )
94
+ return f"{current}\n\n{preset_line}\n\n{buildings_note}"
95
+
96
+
97
+ def on_preset_change(preset, distance_m, buildings_enabled):
98
+ """
99
+ When preset changes:
100
+ - If not Custom, force distance slider to preset value
101
+ - Update buildings availability accordingly
102
+ """
103
+ preset_dist = preset_to_distance(preset)
104
+ if preset_dist is not None:
105
+ distance_m = preset_dist
106
+
107
+ allowed = buildings_allowed(preset, distance_m)
108
+ if not allowed:
109
+ buildings_enabled = False
110
+
111
+ hint = format_distance_hint(preset, distance_m)
112
+
113
+ return (
114
+ gr.update(value=int(distance_m)), # distance slider
115
+ gr.update(interactive=allowed, value=buildings_enabled), # buildings checkbox
116
+ gr.update(value=hint), # hint markdown
117
+ )
118
+
119
+
120
+ def on_distance_change(distance_m, preset, buildings_enabled):
121
+ """
122
+ When distance slider changes:
123
+ - If it matches a preset exactly => set preset accordingly
124
+ - Else => set preset to Custom
125
+ - Update buildings availability accordingly
126
+ """
127
+ new_preset = distance_to_preset(distance_m)
128
+ preset = new_preset
129
+
130
+ allowed = buildings_allowed(preset, distance_m)
131
+ if not allowed:
132
+ buildings_enabled = False
133
+
134
+ hint = format_distance_hint(preset, distance_m)
135
+
136
+ return (
137
+ gr.update(value=preset), # preset dropdown
138
+ gr.update(interactive=allowed, value=buildings_enabled), # buildings checkbox
139
+ gr.update(value=hint), # hint markdown
140
+ )
141
 
142
 
143
  def generate(
 
152
  fast_mode,
153
  map_format,
154
  orientation,
155
+ buildings_enabled,
156
+ railroads_enabled, # <-- NEW
157
  ):
158
  """
159
  Run the poster script and stream logs into the UI.
 
169
  if fast_mode:
170
  distance_m = min(int(distance_m), 12000)
171
 
172
+ # Plan format is always square (orientation ignored)
173
  if map_format == "plan":
174
  orientation = "portrait"
175
 
 
181
  "--orientation", orientation,
182
  ]
183
 
184
+ # Buildings flag (only if allowed + enabled)
185
+ if buildings_enabled and buildings_allowed(preset, distance_m):
186
+ cmd += ["--buildings"]
187
+
188
+ # Railroads flag (NEW)
189
+ if railroads_enabled:
190
+ cmd += ["--railroads"]
191
+
192
  # Center selection
193
  if center_mode == "Coordinates":
 
194
  if lat is None or lon is None:
195
  yield "❌ Please provide both latitude and longitude.", None
196
  return
197
+
198
  cmd += ["--lat", str(float(lat)), "--lon", str(float(lon))]
199
 
200
+ # City/Country become labels (optional)
201
  if city:
202
  cmd += ["--city", city]
203
  if country:
 
205
 
206
  cmd_note = f"Center: Coordinates (lat={lat}, lon={lon})"
207
  else:
 
208
  if not city or not country:
209
  yield "❌ Please provide both City and Country (or switch to Coordinates).", None
210
  return
211
+
212
  cmd += ["--city", city, "--country", country]
213
  cmd_note = f"Center: City ({city}, {country})"
214
 
 
218
  status_lines.append("🚀 Starting… (OSM downloads can be slow on shared Spaces)")
219
  status_lines.append(cmd_note)
220
  status_lines.append(f"Command: {' '.join(cmd)}")
221
+ status_lines.append("Tip: progress may sit at 0% for a while, then jump (normal).")
222
  yield "\n".join(status_lines), None
223
 
224
  proc = subprocess.Popen(
 
235
  if not line:
236
  continue
237
  status_lines.append(line)
 
238
  if len(status_lines) > 250:
239
  status_lines = status_lines[-250:]
 
240
  yield "\n".join(status_lines), None
241
  finally:
242
  rc = proc.wait()
 
257
 
258
 
259
  def on_format_change(fmt):
260
+ """Disable orientation control when Plan is selected."""
261
  if fmt == "plan":
262
  return gr.update(value="portrait", interactive=False)
263
  return gr.update(interactive=True)
 
267
  """Show lat/lon inputs only when Coordinates is selected."""
268
  if mode == "Coordinates":
269
  return (
270
+ gr.update(visible=True), # city label still useful
271
  gr.update(visible=True),
272
  gr.update(visible=True),
273
  gr.update(visible=True),
274
  )
 
275
  return (
276
  gr.update(visible=True),
277
  gr.update(visible=True),
 
281
 
282
 
283
  with gr.Blocks(css=APP_CSS) as demo:
284
+ gr.Markdown("# MapToPoster – Hugging Face Demo")
 
 
 
 
 
 
 
 
 
285
 
286
  center_mode = gr.Radio(
287
  label="Center",
 
301
  on_center_mode_change,
302
  inputs=[center_mode],
303
  outputs=[city, country, lat, lon],
304
+ queue=False, # <- UI interaction: do NOT queue
305
  )
306
 
307
+ theme = gr.Dropdown(
308
+ label="Theme",
309
+ choices=THEMES,
310
+ value=(THEMES[0] if THEMES else None),
311
+ )
 
312
 
313
  with gr.Row():
314
  preset = gr.Dropdown(
 
321
  minimum=1000,
322
  maximum=20000,
323
  step=250,
324
+ value=PRESET_DIST["City"],
325
  )
326
 
327
+ buildings = gr.Checkbox(
328
+ label="Buildings layer (small scale only)",
329
+ value=False,
330
+ interactive=False, # will be enabled automatically when allowed
331
+ )
332
+
333
+ # NEW: railroads option
334
+ railroads = gr.Checkbox(
335
+ label="Railroads layer",
336
+ value=False,
337
+ )
338
+
339
+ distance_hint = gr.Markdown(format_distance_hint("City", PRESET_DIST["City"]))
340
+
341
+ preset.change(
342
+ on_preset_change,
343
+ inputs=[preset, distance, buildings],
344
+ outputs=[distance, buildings, distance_hint],
345
+ queue=False, # <- UI interaction: do NOT queue
346
+ )
347
+
348
+ distance.change(
349
+ on_distance_change,
350
+ inputs=[distance, preset, buildings],
351
+ outputs=[preset, buildings, distance_hint],
352
+ queue=False, # <- UI interaction: do NOT queue
353
+ )
354
+
355
  with gr.Row():
356
  map_format = gr.Dropdown(
357
  label="Format",
 
364
  value="portrait",
365
  )
366
 
367
+ map_format.change(
368
+ on_format_change,
369
+ inputs=[map_format],
370
+ outputs=[orientation],
371
+ queue=False, # <- UI interaction: do NOT queue
372
+ )
373
 
374
  fast_mode = gr.Checkbox(label="Fast mode (cap ~12km)", value=True)
375
 
 
379
 
380
  btn.click(
381
  generate,
382
+ inputs=[
383
+ center_mode, city, country, lat, lon,
384
+ theme, preset, distance, fast_mode,
385
+ map_format, orientation,
386
+ buildings,
387
+ railroads, # <-- NEW
388
+ ],
389
  outputs=[status, out],
390
+ # leave default queuing behavior for the heavy job
391
  )
392
 
393
  demo.launch()