TahaRasouli commited on
Commit
a42bc84
Β·
verified Β·
1 Parent(s): 9fd0d4c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +47 -67
app.py CHANGED
@@ -21,17 +21,9 @@ Run:
21
  python app.py
22
  """
23
 
24
- import os
25
- import tempfile
26
- import time
27
-
28
  import gradio as gr
29
  import folium
30
  import openpyxl
31
- from selenium import webdriver
32
- from selenium.webdriver.chrome.options import Options as ChromeOptions
33
- from selenium.webdriver.support.ui import WebDriverWait
34
- from selenium.common.exceptions import TimeoutException, WebDriverException
35
 
36
  import state_lookup
37
 
@@ -45,7 +37,6 @@ COLOR_PALETTE = [
45
  _cached_filepath = None
46
  _cached_section_configs = None
47
  _cached_data = None
48
- _last_map_html = None # full standalone HTML of the most recently generated map, for PNG export
49
 
50
 
51
  def parse_coords(coord_str):
@@ -234,6 +225,48 @@ def build_map_legend(section_configs):
234
  )
235
 
236
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
237
  def build_sidebar_legend(section_configs):
238
  if not section_configs:
239
  return "<p><em>Upload a file to see the colour legend.</em></p>"
@@ -387,54 +420,9 @@ def generate_map(
387
  <span style="color:#555">{stats_text}</span>
388
  </div>"""))
389
 
390
- global _last_map_html
391
- _last_map_html = fig.render()
392
- return fig._repr_html_()
393
 
394
-
395
- def export_map_png():
396
- """Render the last generated map with headless Chrome and return a PNG file path."""
397
- if _last_map_html is None:
398
- raise gr.Error("Generate a map first, then export it.")
399
-
400
- tmp_dir = tempfile.mkdtemp(prefix="d2m_")
401
- html_path = os.path.join(tmp_dir, "map.html")
402
- png_path = os.path.join(tmp_dir, "map.png")
403
- with open(html_path, "w", encoding="utf-8") as f:
404
- f.write(_last_map_html)
405
-
406
- opts = ChromeOptions()
407
- opts.add_argument("--headless=new")
408
- opts.add_argument("--no-sandbox")
409
- opts.add_argument("--disable-dev-shm-usage")
410
- opts.add_argument("--hide-scrollbars")
411
- opts.add_argument("--window-size=1900,1200")
412
-
413
- try:
414
- driver = webdriver.Chrome(options=opts)
415
- except WebDriverException as e:
416
- raise gr.Error(
417
- "Could not start headless Chrome for the export. "
418
- f"Make sure Google Chrome or Chromium is installed. ({e})"
419
- )
420
-
421
- try:
422
- driver.get(f"file://{html_path}")
423
- try:
424
- WebDriverWait(driver, 10).until(
425
- lambda d: d.execute_script(
426
- "return document.querySelectorAll('.leaflet-tile-loading').length === 0 "
427
- "&& document.querySelectorAll('.leaflet-tile-loaded').length > 0"
428
- )
429
- )
430
- except TimeoutException:
431
- pass # export whatever tiles did load rather than failing outright
432
- time.sleep(0.5)
433
- driver.save_screenshot(png_path)
434
- finally:
435
- driver.quit()
436
-
437
- return gr.update(value=png_path, visible=True)
438
 
439
 
440
  # ──────────────────────────────────────────────
@@ -522,11 +510,9 @@ with gr.Blocks(title="Standorte Karte Deutschland", theme=gr.themes.Soft()) as d
522
  value="<p style='color:#888;padding:40px;text-align:center;'>"
523
  "Upload a file and click <b>Generate Map</b> to begin.</p>",
524
  )
525
- export_btn = gr.Button("πŸ“Έ Export Map as PNG")
526
- export_image = gr.Image(
527
- label="Exported PNG (hover for the download button)",
528
- interactive=False,
529
- visible=False,
530
  )
531
 
532
  # ── Event wiring ─────────────────────────────────────────────────────────
@@ -574,12 +560,6 @@ with gr.Blocks(title="Standorte Karte Deutschland", theme=gr.themes.Soft()) as d
574
  outputs=[map_output],
575
  )
576
 
577
- export_btn.click(
578
- fn=export_map_png,
579
- inputs=[],
580
- outputs=[export_image],
581
- )
582
-
583
 
584
  if __name__ == "__main__":
585
  demo.launch(share=False, server_port=7860, inbrowser=True)
 
21
  python app.py
22
  """
23
 
 
 
 
 
24
  import gradio as gr
25
  import folium
26
  import openpyxl
 
 
 
 
27
 
28
  import state_lookup
29
 
 
37
  _cached_filepath = None
38
  _cached_section_configs = None
39
  _cached_data = None
 
40
 
41
 
42
  def parse_coords(coord_str):
 
225
  )
226
 
227
 
228
+ def build_export_button():
229
+ """A button + inline script that rasterizes the map (tiles, markers, overlays) to a
230
+ downloadable PNG entirely in the visitor's browser via html2canvas β€” no server-side
231
+ headless browser needed, so it works the same locally and on hosted deployments."""
232
+ return """
233
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js"></script>
234
+ <button id="d2m-export-btn" onclick="d2mExportPNG()" style="
235
+ position:fixed;top:10px;left:50%;transform:translateX(-50%);z-index:1000;
236
+ background:white;border:1px solid #ccc;border-radius:6px;padding:8px 14px;
237
+ font-size:13px;cursor:pointer;box-shadow:2px 2px 6px rgba(0,0,0,0.2);">
238
+ πŸ“₯ Download PNG
239
+ </button>
240
+ <script>
241
+ function d2mExportPNG() {
242
+ var btn = document.getElementById('d2m-export-btn');
243
+ var originalText = btn.innerHTML;
244
+ if (typeof html2canvas === 'undefined') {
245
+ alert('Export library failed to load β€” check your internet connection and try again.');
246
+ return;
247
+ }
248
+ btn.innerHTML = '⏳ Rendering…';
249
+ btn.disabled = true;
250
+ btn.style.visibility = 'hidden';
251
+ html2canvas(document.body, {useCORS: true, backgroundColor: '#ffffff'}).then(function(canvas) {
252
+ var link = document.createElement('a');
253
+ link.download = 'standorte-map.png';
254
+ link.href = canvas.toDataURL('image/png');
255
+ document.body.appendChild(link);
256
+ link.click();
257
+ document.body.removeChild(link);
258
+ }).catch(function(err) {
259
+ alert('Export failed: ' + err);
260
+ }).finally(function() {
261
+ btn.innerHTML = originalText;
262
+ btn.disabled = false;
263
+ btn.style.visibility = 'visible';
264
+ });
265
+ }
266
+ </script>
267
+ """
268
+
269
+
270
  def build_sidebar_legend(section_configs):
271
  if not section_configs:
272
  return "<p><em>Upload a file to see the colour legend.</em></p>"
 
420
  <span style="color:#555">{stats_text}</span>
421
  </div>"""))
422
 
423
+ m.get_root().html.add_child(folium.Element(build_export_button()))
 
 
424
 
425
+ return fig._repr_html_()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
426
 
427
 
428
  # ──────────────────────────────────────────────
 
510
  value="<p style='color:#888;padding:40px;text-align:center;'>"
511
  "Upload a file and click <b>Generate Map</b> to begin.</p>",
512
  )
513
+ gr.Markdown(
514
+ "_Use the **πŸ“₯ Download PNG** button at the top of the generated map "
515
+ "to save it as an image._"
 
 
516
  )
517
 
518
  # ── Event wiring ─────────────────────────────────────────────────────────
 
560
  outputs=[map_output],
561
  )
562
 
 
 
 
 
 
 
563
 
564
  if __name__ == "__main__":
565
  demo.launch(share=False, server_port=7860, inbrowser=True)