pvbang commited on
Commit
f3f8e56
·
verified ·
1 Parent(s): a98a9cc

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +477 -0
app.py ADDED
@@ -0,0 +1,477 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import re
2
+ import streamlit as st
3
+ import folium
4
+ from folium import Marker, PolyLine, DivIcon
5
+ from geopy.distance import geodesic
6
+ import requests
7
+ import polyline
8
+ import math
9
+ import streamlit.components.v1 as components
10
+ from streamlit_folium import st_folium # Cài đặt: pip install streamlit-folium
11
+
12
+ # Lệnh này phải là dòng đầu tiên trong script của bạn
13
+ st.set_page_config(layout="wide", page_title="Làm Map cho Chị")
14
+
15
+ # Thêm FontAwesome CSS để hiển thị icon
16
+ st.markdown(
17
+ '<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">',
18
+ unsafe_allow_html=True,
19
+ )
20
+
21
+ # -----------------------------
22
+ # Hàm mở rộng link và trích xuất tọa độ từ URL
23
+ # -----------------------------
24
+ def extract_coords_from_url(url):
25
+ try:
26
+ # Mở URL để lấy URL đích sau redirect
27
+ resp = requests.get(url, allow_redirects=True, timeout=5)
28
+ final_url = resp.url
29
+ # Tìm kiếm tọa độ theo mẫu "@lat,lon"
30
+ match = re.search(r'@([\d\.\-]+),([\d\.\-]+)', final_url)
31
+ if match:
32
+ return (float(match.group(1)), float(match.group(2)))
33
+ # Nếu không tìm thấy, thử mẫu "!3dlat!4dlon"
34
+ match2 = re.search(r'!3d([\d\.\-]+)!4d([\d\.\-]+)', final_url)
35
+ if match2:
36
+ return (float(match2.group(1)), float(match2.group(2)))
37
+ except Exception as e:
38
+ st.error("Lỗi khi mở link: " + str(e))
39
+ return None
40
+
41
+
42
+ # -----------------------------
43
+ # Danh sách icon (FontAwesome)
44
+ # -----------------------------
45
+ icon_list = [
46
+ "glass", "music", "search", "envelope-o", "heart", "star", "star-o", "user",
47
+ "film", "th-large", "th", "th-list", "check", "times", "search-plus", "search-minus",
48
+ "power-off", "signal", "cog", "trash-o", "home", "file-o", "clock-o", "road", "download",
49
+ "arrow-circle-o-down", "arrow-circle-o-up", "inbox", "play-circle-o", "repeat", "refresh",
50
+ "list-alt", "lock", "flag", "headphones", "volume-off", "volume-down", "volume-up",
51
+ "qrcode", "barcode", "tag", "tags", "book", "bookmark", "print", "camera", "font", "bold",
52
+ "italic", "text-height", "text-width", "align-left", "align-center", "align-right",
53
+ "align-justify", "list", "outdent", "indent", "video-camera", "picture-o", "pencil",
54
+ "map-marker", "adjust", "tint", "pencil-square-o", "share", "check-square-o", "arrows",
55
+ "step-backward", "fast-backward", "backward", "play", "pause", "stop", "forward",
56
+ "fast-forward", "step-forward", "eject", "chevron-left", "chevron-right", "plus-circle",
57
+ "minus-circle", "times-circle", "check-circle", "question-circle", "info-circle",
58
+ "crosshairs", "times-circle-o", "check-circle-o", "ban", "arrow-left", "arrow-right",
59
+ "arrow-up", "arrow-down", "share-square-o", "expand", "compress", "plus", "minus",
60
+ "asterisk", "exclamation-circle", "gift", "leaf", "fire", "eye", "eye-slash",
61
+ "exclamation-triangle", "plane", "calendar", "random", "comment", "magnet", "chevron-up",
62
+ "chevron-down", "retweet", "shopping-cart", "folder", "folder-open", "arrows-v",
63
+ "arrows-h", "bar-chart", "twitter-square", "facebook-square", "camera-retro", "key",
64
+ "cogs", "comments"
65
+ ]
66
+
67
+ # -----------------------------
68
+ # Khởi tạo dữ liệu địa chỉ mẫu trong session_state
69
+ # -----------------------------
70
+ if "addresses" not in st.session_state:
71
+ st.session_state.addresses = [
72
+ {
73
+ "name": "11 Trương Hữu Hoàng",
74
+ "latitude": 16.491013,
75
+ "longitude": 107.622923,
76
+ "marker_icon": "home",
77
+ "route_color": "#FF4500",
78
+ "is_home": True,
79
+ "visible": True
80
+ },
81
+ {
82
+ "name": "KQH Vinh Vệ",
83
+ "latitude": 16.493565250557587,
84
+ "longitude": 107.6274186,
85
+ "marker_icon": "map-marker",
86
+ "route_color": "#8A2BE2",
87
+ "is_home": False,
88
+ "visible": True
89
+ },
90
+ {
91
+ "name": "UBND Xã Phú Mỹ",
92
+ "latitude": 16.49506178728241,
93
+ "longitude": 107.6298651576717,
94
+ "marker_icon": "building",
95
+ "route_color": "#1E90FF",
96
+ "is_home": False,
97
+ "visible": True
98
+ },
99
+ {
100
+ "name": "KQH Chiết Bi",
101
+ "latitude": 16.49102143751169,
102
+ "longitude": 107.60960879999999,
103
+ "marker_icon": "map-marker",
104
+ "route_color": "#8A2BE2",
105
+ "is_home": False,
106
+ "visible": True
107
+ },
108
+ {
109
+ "name": "Trường Tiểu học Phú Thượng",
110
+ "latitude": 16.491607862678077,
111
+ "longitude": 107.60640322883584,
112
+ "marker_icon": "school",
113
+ "route_color": "#32CD32",
114
+ "is_home": False,
115
+ "visible": True
116
+ },
117
+ {
118
+ "name": "Trường THCS Phú Thượng",
119
+ "latitude": 16.491657525074864,
120
+ "longitude": 107.60404525582075,
121
+ "marker_icon": "school",
122
+ "route_color": "#0000FF",
123
+ "is_home": False,
124
+ "visible": True
125
+ },
126
+ {
127
+ "name": "Trường UKA - UK Academy Huế",
128
+ "latitude": 16.493026837831497,
129
+ "longitude": 107.60477552883586,
130
+ "marker_icon": "graduation-cap",
131
+ "route_color": "#FF8C00",
132
+ "is_home": False,
133
+ "visible": True
134
+ },
135
+ {
136
+ "name": "Trường Nghiệp Vụ Thuế",
137
+ "latitude": 16.4956182,
138
+ "longitude": 107.60249869999998,
139
+ "marker_icon": "graduation-cap",
140
+ "route_color": "#800080",
141
+ "is_home": False,
142
+ "visible": True
143
+ },
144
+ {
145
+ "name": "Trường Tiểu học Thuỷ Vân",
146
+ "latitude": 16.489429637257857,
147
+ "longitude": 107.62618450000001,
148
+ "marker_icon": "school",
149
+ "route_color": "#32CD32",
150
+ "is_home": False,
151
+ "visible": True
152
+ },
153
+ {
154
+ "name": "Chợ Hôm (Dạ Lê Chánh)",
155
+ "latitude": 16.490582399855406,
156
+ "longitude": 107.6265241,
157
+ "marker_icon": "shopping-cart",
158
+ "route_color": "#FFA500",
159
+ "is_home": False,
160
+ "visible": True
161
+ },
162
+ {
163
+ "name": "UBND Phường Thuỷ Vân",
164
+ "latitude": 16.48642452451863,
165
+ "longitude": 107.6235033,
166
+ "marker_icon": "building",
167
+ "route_color": "#1E90FF",
168
+ "is_home": False,
169
+ "visible": True
170
+ },
171
+ {
172
+ "name": "Trường THCS Phú Mỹ",
173
+ "latitude": 16.494371725363457,
174
+ "longitude": 107.62332647116415,
175
+ "marker_icon": "school",
176
+ "route_color": "#0000FF",
177
+ "is_home": False,
178
+ "visible": True
179
+ }
180
+ ]
181
+ else:
182
+ for addr in st.session_state.addresses:
183
+ if "visible" not in addr:
184
+ addr["visible"] = True
185
+
186
+ # -----------------------------
187
+ # Sidebar: Cài đặt kích thước icon và text
188
+ # -----------------------------
189
+ st.sidebar.subheader("Cài đặt kích thước")
190
+ home_icon_size = st.sidebar.slider("Kích thước icon chính (Home)", min_value=16, max_value=48, value=28)
191
+ marker_icon_size = st.sidebar.slider("Kích thước icon địa chỉ (khác)", min_value=12, max_value=32, value=16)
192
+ home_text_size = st.sidebar.slider("Kích thước chữ địa chỉ Home", min_value=12, max_value=36, value=24)
193
+ marker_text_size = st.sidebar.slider("Kích thước chữ địa chỉ (khác)", min_value=10, max_value=24, value=16)
194
+
195
+ # -----------------------------
196
+ # Sidebar: Chọn chế độ hiển thị đường đi
197
+ # -----------------------------
198
+ route_mode = st.sidebar.radio(
199
+ "Chọn chế độ hiển thị đường đi",
200
+ ("Tuyến đường ngắn nhất", "Đường thẳng"),
201
+ index=1
202
+ )
203
+
204
+ # Chọn icon và hiển thị preview để cập nhật ngay
205
+ selected_icon = st.sidebar.selectbox("Chọn icon cho địa chỉ", options=icon_list,
206
+ index=icon_list.index("map-marker") if "map-marker" in icon_list else 0)
207
+ st.sidebar.markdown(
208
+ f"<div style='font-size:15px; margin-bottom:10px;'>Xem trước icon: <i class='fa fa-{selected_icon}'></i></div>",
209
+ unsafe_allow_html=True
210
+ )
211
+
212
+ # -----------------------------
213
+ # Sidebar: Form thêm địa chỉ mới với live preview icon
214
+ # -----------------------------
215
+ st.sidebar.header("Thêm Địa Chỉ Mới")
216
+
217
+ # Nhập link Google Map (có thể là link rút gọn, ví dụ: https://maps.app.goo.gl/oMS72uF8bP2b6gmbA)
218
+ google_map_link = st.sidebar.text_input("Nhập tọa độ từ link Google Map", value="", placeholder="https://maps.app.goo.gl/oMS72uF8bP2b6gmbA")
219
+
220
+ if google_map_link:
221
+ coords = extract_coords_from_url(google_map_link)
222
+ if coords:
223
+ st.session_state.selected_coords = coords
224
+ st.sidebar.success(f"Tọa độ được trích xuất: {coords[0]}, {coords[1]}")
225
+ else:
226
+ st.sidebar.error("Không thể trích xuất tọa độ từ link này.")
227
+
228
+ # Sử dụng tọa độ đã chọn làm giá trị mặc định cho vĩ độ và kinh độ
229
+ default_coords = st.session_state.get("selected_coords", (16.460000, 107.580000))
230
+ with st.sidebar.form("add_address_form"):
231
+ name = st.text_input("Tên địa chỉ")
232
+ latitude = st.number_input("Vĩ độ", value=default_coords[0], format="%.6f")
233
+ longitude = st.number_input("Kinh độ", value=default_coords[1], format="%.6f")
234
+ route_color = st.color_picker("Chọn màu cho đường đi", value="#3388ff")
235
+ is_home = st.checkbox("Đặt làm địa chỉ chính (Home)", value=False)
236
+ submitted = st.form_submit_button("Thêm địa chỉ")
237
+ if submitted:
238
+ if not name:
239
+ st.sidebar.error("Vui lòng nhập tên địa chỉ!")
240
+ else:
241
+ if is_home:
242
+ for addr in st.session_state.addresses:
243
+ addr["is_home"] = False
244
+ new_address = {
245
+ "name": name,
246
+ "latitude": latitude,
247
+ "longitude": longitude,
248
+ "marker_icon": selected_icon,
249
+ "route_color": route_color,
250
+ "is_home": is_home,
251
+ "visible": True
252
+ }
253
+ st.session_state.addresses.append(new_address)
254
+ st.sidebar.success("Đã thêm địa chỉ mới.")
255
+
256
+ # -----------------------------
257
+ # Sidebar: Danh sách địa chỉ (có khả năng xóa và ẩn/hiện)
258
+ # -----------------------------
259
+ st.sidebar.header("Danh sách địa chỉ")
260
+ for idx, addr in list(enumerate(st.session_state.addresses)):
261
+ label = f"{addr['name']} ({'Home' if addr['is_home'] else 'Địa chỉ'})"
262
+ st.sidebar.write(label)
263
+ col1, col2 = st.sidebar.columns(2)
264
+ if col1.button("Xóa", key=f"delete_{idx}"):
265
+ st.session_state.addresses.pop(idx)
266
+ st.experimental_rerun()
267
+ toggle_label = "Ẩn" if addr.get("visible", True) else "Hiện"
268
+ if col2.button(toggle_label, key=f"toggle_{idx}"):
269
+ st.session_state.addresses[idx]["visible"] = not addr.get("visible", True)
270
+ st.experimental_rerun()
271
+
272
+ # -----------------------------
273
+ # Xác định địa chỉ Home (nếu không có thì chọn địa chỉ đầu tiên)
274
+ # -----------------------------
275
+ if st.session_state.addresses:
276
+ home_address = next((addr for addr in st.session_state.addresses if addr["is_home"]),
277
+ st.session_state.addresses[0])
278
+ else:
279
+ st.error("Chưa có địa chỉ nào. Vui lòng thêm địa chỉ.")
280
+ st.stop()
281
+
282
+ home_coords = [home_address["latitude"], home_address["longitude"]]
283
+
284
+ # -----------------------------
285
+ # Hàm lấy lộ trình từ API OSRM (cho tuyến đường ngắn nhất)
286
+ # -----------------------------
287
+ def get_route(start, end):
288
+ url = f"http://router.project-osrm.org/route/v1/driving/{start[1]},{start[0]};{end[1]},{end[0]}?overview=full&geometries=polyline"
289
+ response = requests.get(url)
290
+ if response.ok:
291
+ data = response.json()
292
+ if data["routes"]:
293
+ route_geometry = data["routes"][0]["geometry"]
294
+ return polyline.decode(route_geometry)
295
+ return None
296
+
297
+ # -----------------------------
298
+ # Hàm tính vector offset (chuyển offset từ mét sang độ)
299
+ # -----------------------------
300
+ def compute_offset_vector(home, dest, offset_meters):
301
+ lat1, lon1 = home
302
+ lat2, lon2 = dest
303
+ mean_lat = math.radians((lat1 + lat2) / 2)
304
+ meters_per_deg_lat = 111111
305
+ meters_per_deg_lon = 111111 * math.cos(mean_lat)
306
+
307
+ d_lat = (lat2 - lat1) * meters_per_deg_lat
308
+ d_lon = (lon2 - lon1) * meters_per_deg_lon
309
+
310
+ perp_x = -d_lat
311
+ perp_y = d_lon
312
+ norm = math.sqrt(perp_x ** 2 + perp_y ** 2)
313
+ if norm == 0:
314
+ return (0, 0)
315
+ unit_x = perp_x / norm
316
+ unit_y = perp_y / norm
317
+ offset_x = unit_x * offset_meters
318
+ offset_y = unit_y * offset_meters
319
+ offset_lat = offset_y / meters_per_deg_lat
320
+ offset_lon = offset_x / meters_per_deg_lon
321
+ return (offset_lat, offset_lon)
322
+
323
+ def apply_offset_to_polyline(polyline_pts, offset_meters, home, dest):
324
+ offset = compute_offset_vector(home, dest, offset_meters)
325
+ return [[pt[0] + offset[0], pt[1] + offset[1]] for pt in polyline_pts]
326
+
327
+ # -----------------------------
328
+ # Xử lý các địa chỉ có tọa độ gần nhau để tránh vẽ đường trùng lặp
329
+ # -----------------------------
330
+ def get_overlap_key(addr):
331
+ return (round(addr["latitude"], 4), round(addr["longitude"], 4))
332
+
333
+ overlap_groups = {}
334
+ for idx, addr in enumerate(st.session_state.addresses):
335
+ if not addr["is_home"]:
336
+ key = get_overlap_key(addr)
337
+ overlap_groups.setdefault(key, []).append(idx)
338
+
339
+ offset_mapping = {}
340
+ for group in overlap_groups.values():
341
+ n = len(group)
342
+ for j, addr_index in enumerate(group):
343
+ offset_mapping[addr_index] = (j - (n - 1) / 2) * 10
344
+
345
+ # -----------------------------
346
+ # Tạo bản đồ với Folium hiển thị marker và tuyến đường
347
+ # -----------------------------
348
+ m = folium.Map(location=home_coords, zoom_start=14, tiles="CartoDB Positron")
349
+
350
+ # Vẽ marker cho từng địa chỉ (chỉ những địa chỉ visible)
351
+ for idx, addr in enumerate(st.session_state.addresses):
352
+ if not addr.get("visible", True):
353
+ continue
354
+ coords = [addr["latitude"], addr["longitude"]]
355
+ if addr["is_home"]:
356
+ icon_html = f'''
357
+ <div style="display: inline-flex; align-items: center; font-family: 'Lora', serif;">
358
+ <div style="background: white; border: 2px solid {addr["route_color"]}; border-radius: 50%; padding: 8px;">
359
+ <i class="fa fa-{addr["marker_icon"]}" style="font-size:{home_icon_size}px; color:{addr["route_color"]};"></i>
360
+ </div>
361
+ <div style="margin-left: 8px; font-size:{home_text_size}px; font-weight:bold; color:black; white-space: nowrap;">
362
+ {addr["name"]}
363
+ </div>
364
+ </div>
365
+ '''
366
+ Marker(
367
+ location=coords,
368
+ tooltip=addr["name"],
369
+ icon=DivIcon(html=icon_html),
370
+ draggable=True
371
+ ).add_to(m)
372
+ else:
373
+ icon_html = f'''
374
+ <div style="text-align:center; font-family: 'Lora', serif;">
375
+ <div style="background: white; border: 2px solid {addr["route_color"]}; border-radius: 50%; display: inline-block; padding: 8px;">
376
+ <i class="fa fa-{addr["marker_icon"]}" style="font-size:{marker_icon_size}px; color:{addr["route_color"]};"></i>
377
+ </div>
378
+ <div style="margin-top:4px; font-size:{marker_text_size}px; color:black; white-space: nowrap;">
379
+ {addr["name"]}
380
+ </div>
381
+ </div>
382
+ '''
383
+ Marker(
384
+ location=coords,
385
+ tooltip=addr["name"],
386
+ icon=DivIcon(html=icon_html),
387
+ draggable=True
388
+ ).add_to(m)
389
+
390
+ # Vẽ tuyến đường từ Home đến các địa chỉ khác (chỉ với địa chỉ visible)
391
+ for idx, addr in enumerate(st.session_state.addresses):
392
+ if addr["is_home"] or not addr.get("visible", True):
393
+ continue
394
+ dest_coords = [addr["latitude"], addr["longitude"]]
395
+ offset_m = offset_mapping.get(idx, 0)
396
+ if route_mode == "Tuyến đường ngắn nhất":
397
+ route_pts = get_route(home_coords, dest_coords)
398
+ if route_pts:
399
+ if offset_m != 0:
400
+ route_pts = apply_offset_to_polyline(route_pts, offset_m, home_coords, dest_coords)
401
+ distance = geodesic(home_coords, dest_coords).kilometers
402
+ popup_text = f"{addr['name']} - {distance:.1f} Km"
403
+ PolyLine(
404
+ locations=route_pts,
405
+ color=addr["route_color"],
406
+ weight=3,
407
+ opacity=0.5,
408
+ popup=popup_text
409
+ ).add_to(m)
410
+ mid_point = route_pts[len(route_pts) // 2]
411
+ Marker(
412
+ location=mid_point,
413
+ draggable=True,
414
+ icon=DivIcon(
415
+ html=f'''<div style="font-family: 'Lora', serif; font-size:12pt; color:{addr["route_color"]}; font-weight:bold;
416
+ background-color:rgba(255,255,255,0.7); padding:2px 4px; border-radius:4px; text-align:center; white-space: nowrap;">
417
+ {distance:.1f} Km
418
+ </div>'''
419
+ )
420
+ ).add_to(m)
421
+ elif route_mode == "Đường thẳng":
422
+ route_pts = [home_coords, dest_coords]
423
+ if offset_m != 0:
424
+ route_pts = apply_offset_to_polyline(route_pts, offset_m, home_coords, dest_coords)
425
+ distance = geodesic(home_coords, dest_coords).kilometers
426
+ popup_text = f"{addr['name']} - {distance:.1f} Km"
427
+ PolyLine(
428
+ locations=route_pts,
429
+ color=addr["route_color"],
430
+ weight=3,
431
+ opacity=0.5,
432
+ popup=popup_text
433
+ ).add_to(m)
434
+ mid_point = [(home_coords[0] + dest_coords[0]) / 2, (home_coords[1] + dest_coords[1]) / 2]
435
+ Marker(
436
+ location=mid_point,
437
+ draggable=True,
438
+ icon=DivIcon(
439
+ html=f'''<div style="font-family: 'Lora', serif; font-size:12pt; color:{addr["route_color"]}; font-weight:bold;
440
+ background-color:rgba(255,255,255,0.7); padding:2px 4px; border-radius:4px; text-align:center; white-space: nowrap;">
441
+ {distance:.1f} Km
442
+ </div>'''
443
+ )
444
+ ).add_to(m)
445
+
446
+ # -----------------------------
447
+ # Render map chính
448
+ # -----------------------------
449
+ map_html = m.get_root().render()
450
+ full_html = f"""
451
+ <html>
452
+ <head>
453
+ <meta charset="utf-8">
454
+ <link href="https://fonts.googleapis.com/css2?family=Lora&display=swap" rel="stylesheet">
455
+ <style>
456
+ html, body {{
457
+ margin: 0;
458
+ padding: 0;
459
+ height: 100%;
460
+ width: 100%;
461
+ font-family: 'Lora', serif;
462
+ }}
463
+ #map_container {{
464
+ height: 100vh;
465
+ width: 100vw;
466
+ }}
467
+ </style>
468
+ </head>
469
+ <body>
470
+ <div id="map_container">
471
+ {map_html}
472
+ </div>
473
+ </body>
474
+ </html>
475
+ """
476
+
477
+ components.html(full_html, height=800, scrolling=True)