Gordon Li commited on
Commit
af1d6b2
·
1 Parent(s): 4a995f4

Discount for near traffic spot

Browse files
Files changed (4) hide show
  1. AirbnbMapVisualiser.py +62 -14
  2. TrafficSpot.py +158 -13
  3. app.py +74 -1
  4. style.css +1 -1
AirbnbMapVisualiser.py CHANGED
@@ -6,8 +6,10 @@ from sentence_transformers import SentenceTransformer, util
6
  from geopy.distance import geodesic
7
  import logging
8
 
 
9
  from TrafficSpot import TrafficSpotManager
10
 
 
11
  class AirbnbMapVisualiser:
12
  def __init__(self):
13
  self.connection_params = {
@@ -25,7 +27,7 @@ class AirbnbMapVisualiser:
25
  getmode=oracledb.SPOOL_ATTRVAL_WAIT
26
  )
27
 
28
- # Initialize TrafficSpotManager
29
  self.traffic_manager = TrafficSpotManager(self.connection_params)
30
  logging.info(f"Traffic spots initialized, {len(self.traffic_manager.traffic_spots)} spots loaded")
31
 
@@ -298,6 +300,7 @@ class AirbnbMapVisualiser:
298
  return [0.0] * len(df)
299
 
300
  def sort_by_relevance(self, df, search_query):
 
301
  if not search_query:
302
  return df
303
 
@@ -365,7 +368,7 @@ class AirbnbMapVisualiser:
365
  return df.sort_values('relevance_score', ascending=False)
366
 
367
  def create_map_and_data(self, neighborhood="Sha Tin", show_traffic=True, center_lat=None, center_lng=None,
368
- selected_id=None, search_query=None):
369
  listings = self.get_neighborhood_listings(neighborhood)
370
 
371
  if not listings:
@@ -400,26 +403,65 @@ class AirbnbMapVisualiser:
400
  tiles='OpenStreetMap'
401
  )
402
 
403
- # First add all traffic spots if enabled
404
- if show_traffic:
405
- self.traffic_manager.add_spots_to_map(m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
 
407
  # Create a feature group for connection lines
408
  lines_group = folium.FeatureGroup(name="Connection Lines")
409
  m.add_child(lines_group)
410
 
411
- # Add Airbnb markers
 
 
 
 
412
  for idx, row in df.iterrows():
413
  marker_id = f"marker_{row['id']}"
414
  reviews = self.get_listing_reviews(row['id'])
415
  review_button_key = f"review_btn_{row['id']}"
416
 
417
- # Find nearest traffic spot within 1 km
418
- nearest_spot, distance = self.find_nearest_traffic_spot(row['latitude'], row['longitude'])
419
-
420
- # Add traffic spot information to popup if found
421
  traffic_spot_info = ""
422
- if nearest_spot:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  # Format distance for display (convert to meters if less than 1km)
424
  distance_str = f"{distance:.2f} km" if distance >= 0.1 else f"{distance * 1000:.0f} meters"
425
 
@@ -433,7 +475,7 @@ class AirbnbMapVisualiser:
433
  </div>
434
  """
435
 
436
- # Add a line connecting Airbnb to nearest traffic spot
437
  folium.PolyLine(
438
  locations=[
439
  [row['latitude'], row['longitude']],
@@ -460,13 +502,19 @@ class AirbnbMapVisualiser:
460
  </div>
461
  """
462
 
 
 
 
 
 
463
  popup_content = f"""
464
  <div style='min-width: 280px; max-width: 320px; padding: 15px;'>
465
  <h4 style='margin: 0 0 10px 0; color: #2c3e50;'>{escape(str(row['name']))}</h4>
466
  <p style='margin: 5px 0;'><strong>Host:</strong> {escape(str(row['host_name']))}</p>
467
  <p style='margin: 5px 0;'><strong>Room Type:</strong> {escape(str(row['room_type']))}</p>
468
- <p style='margin: 5px 0;'><strong>Price:</strong> ${row['price']:.0f}</p>
469
  <p style='margin: 5px 0;'><strong>Reviews:</strong> {row['number_of_reviews']:.0f}</p>
 
470
  {traffic_spot_info}
471
  {relevance_info}
472
  </div>
@@ -506,7 +554,7 @@ class AirbnbMapVisualiser:
506
  </script>
507
  """).add_to(m)
508
 
509
- # Add layer control to toggle traffic spots and connection lines
510
  folium.LayerControl().add_to(m)
511
 
512
  return m, df
 
6
  from geopy.distance import geodesic
7
  import logging
8
 
9
+ # Import the TrafficSpotManager from TrafficSpot module
10
  from TrafficSpot import TrafficSpotManager
11
 
12
+
13
  class AirbnbMapVisualiser:
14
  def __init__(self):
15
  self.connection_params = {
 
27
  getmode=oracledb.SPOOL_ATTRVAL_WAIT
28
  )
29
 
30
+ # Initialize TrafficSpotManager with minimal data
31
  self.traffic_manager = TrafficSpotManager(self.connection_params)
32
  logging.info(f"Traffic spots initialized, {len(self.traffic_manager.traffic_spots)} spots loaded")
33
 
 
300
  return [0.0] * len(df)
301
 
302
  def sort_by_relevance(self, df, search_query):
303
+ """Sort listings by relevance using sentence transformer comparison"""
304
  if not search_query:
305
  return df
306
 
 
368
  return df.sort_values('relevance_score', ascending=False)
369
 
370
  def create_map_and_data(self, neighborhood="Sha Tin", show_traffic=True, center_lat=None, center_lng=None,
371
+ selected_id=None, search_query=None, current_page=1, items_per_page=3):
372
  listings = self.get_neighborhood_listings(neighborhood)
373
 
374
  if not listings:
 
403
  tiles='OpenStreetMap'
404
  )
405
 
406
+ # Calculate pagination indices
407
+ total_items = len(df)
408
+ start_idx = (current_page - 1) * items_per_page
409
+ end_idx = min(start_idx + items_per_page, total_items)
410
+
411
+ # Get the current page's listings
412
+ current_page_df = df.iloc[start_idx:end_idx]
413
+
414
+ # Create a list to store all traffic spots we need to display
415
+ all_traffic_spots_to_display = set()
416
+
417
+ # Find nearest traffic spots for ALL listings
418
+ all_nearest_traffic_spots = {}
419
+
420
+ # First find all nearest traffic spots
421
+ for idx, row in df.iterrows():
422
+ nearest_spot, distance = self.find_nearest_traffic_spot(row['latitude'], row['longitude'])
423
+ if nearest_spot:
424
+ all_nearest_traffic_spots[row['id']] = (nearest_spot, distance)
425
+ all_traffic_spots_to_display.add(nearest_spot.key)
426
 
427
  # Create a feature group for connection lines
428
  lines_group = folium.FeatureGroup(name="Connection Lines")
429
  m.add_child(lines_group)
430
 
431
+ # Display all traffic spots
432
+ if show_traffic and all_traffic_spots_to_display:
433
+ self.traffic_manager.add_spots_to_map(m, all_traffic_spots_to_display)
434
+
435
+ # Add all Airbnb markers and connection lines
436
  for idx, row in df.iterrows():
437
  marker_id = f"marker_{row['id']}"
438
  reviews = self.get_listing_reviews(row['id'])
439
  review_button_key = f"review_btn_{row['id']}"
440
 
441
+ # Get traffic spot info if available for this listing
 
 
 
442
  traffic_spot_info = ""
443
+ discount_info = ""
444
+ discounted_price = row['price']
445
+
446
+ # Check if this listing has a nearest traffic spot
447
+ if row['id'] in all_nearest_traffic_spots:
448
+ nearest_spot, distance = all_nearest_traffic_spots[row['id']]
449
+
450
+ # Get discount rate and apply to price
451
+ discount_rate = nearest_spot.get_discount_rate()
452
+ if discount_rate > 0:
453
+ discounted_price = row['price'] * (1 - discount_rate)
454
+ discount_percentage = int(discount_rate * 100)
455
+
456
+ # Format discount info
457
+ discount_info = f"""
458
+ <div style='background-color: #e8f5e9; padding: 8px; margin: 10px 0; border-radius: 4px; border-left: 4px solid #4caf50;'>
459
+ <p style='margin: 2px 0; font-weight: bold; color: #2e7d32;'>🎉 {discount_percentage}% ENV PROTECTION DISCOUNT!</p>
460
+ <p style='margin: 2px 0;'>Original: ${row['price']:.0f} → Now: ${discounted_price:.0f}</p>
461
+ <p style='margin: 2px 0; font-size: 0.85em;'>Avg. {nearest_spot.avg_vehicle_count:.1f} vehicles per observation</p>
462
+ </div>
463
+ """
464
+
465
  # Format distance for display (convert to meters if less than 1km)
466
  distance_str = f"{distance:.2f} km" if distance >= 0.1 else f"{distance * 1000:.0f} meters"
467
 
 
475
  </div>
476
  """
477
 
478
+ # Add connection lines for ALL listings with nearby traffic spots
479
  folium.PolyLine(
480
  locations=[
481
  [row['latitude'], row['longitude']],
 
502
  </div>
503
  """
504
 
505
+ # Show price with strikethrough if discounted
506
+ price_display = f"<strong>Price:</strong> ${row['price']:.0f}"
507
+ if discount_info:
508
+ price_display = f"<strong>Price:</strong> <span style='text-decoration: line-through;'>${row['price']:.0f}</span> <span style='color: #2e7d32; font-weight: bold;'>${discounted_price:.0f}</span>"
509
+
510
  popup_content = f"""
511
  <div style='min-width: 280px; max-width: 320px; padding: 15px;'>
512
  <h4 style='margin: 0 0 10px 0; color: #2c3e50;'>{escape(str(row['name']))}</h4>
513
  <p style='margin: 5px 0;'><strong>Host:</strong> {escape(str(row['host_name']))}</p>
514
  <p style='margin: 5px 0;'><strong>Room Type:</strong> {escape(str(row['room_type']))}</p>
515
+ <p style='margin: 5px 0;'>{price_display}</p>
516
  <p style='margin: 5px 0;'><strong>Reviews:</strong> {row['number_of_reviews']:.0f}</p>
517
+ {discount_info}
518
  {traffic_spot_info}
519
  {relevance_info}
520
  </div>
 
554
  </script>
555
  """).add_to(m)
556
 
557
+ # Add layer control to toggle connection lines
558
  folium.LayerControl().add_to(m)
559
 
560
  return m, df
TrafficSpot.py CHANGED
@@ -2,8 +2,10 @@ import folium
2
  import oracledb
3
  import logging
4
  import base64
 
5
  from html import escape
6
  from datasets import load_dataset
 
7
 
8
 
9
  class TrafficSpot:
@@ -12,14 +14,60 @@ class TrafficSpot:
12
  self.latitude = float(latitude) if latitude is not None else None
13
  self.longitude = float(longitude) if longitude is not None else None
14
  self.dataset_rows = dataset_rows or [] # List of matching dataset rows (up to 5)
 
15
 
16
  def is_valid(self):
17
  return self.latitude is not None and self.longitude is not None
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def create_popup_content(self):
 
 
 
 
 
 
 
 
 
 
 
20
  html = f"""
21
  <div style='min-width: 150px; padding: 10px;'>
22
  <p style='margin: 5px 0;'><strong>Location ID:</strong> {escape(str(self.key))}</p>
 
23
  """
24
 
25
  if self.dataset_rows:
@@ -58,10 +106,18 @@ class TrafficSpot:
58
 
59
  def add_to_map(self, folium_map):
60
  if self.is_valid():
 
 
 
 
 
 
 
 
61
  folium.Marker(
62
  location=[self.latitude, self.longitude],
63
  popup=self.create_popup_content(),
64
- icon=folium.Icon(color='blue', icon='camera'),
65
  ).add_to(folium_map)
66
 
67
 
@@ -69,20 +125,30 @@ class TrafficSpotManager:
69
  def __init__(self, connection_params):
70
  self.connection_params = connection_params
71
  self.traffic_spots = []
72
- self.load_traffic_spots()
 
 
73
 
74
- def load_traffic_spots(self):
 
75
  try:
76
  dataset = load_dataset("slliac/traffic-analysis", split="train")
77
  dataset_list = [row for row in dataset]
78
  dataset_list.sort(key=lambda x: x['capture_time'], reverse=True)
79
 
 
80
  dataset_dict = {}
 
81
  for row in dataset_list:
82
  loc_id = row['location_id']
 
 
 
83
  if loc_id not in dataset_dict:
84
  dataset_dict[loc_id] = []
85
- if len(dataset_dict[loc_id]) < 2:
 
 
86
  dataset_dict[loc_id].append(row)
87
 
88
  unique_locations = list(dataset_dict.keys())
@@ -111,20 +177,99 @@ class TrafficSpotManager:
111
  for spot in spots
112
  ]
113
 
 
 
 
 
114
  conn.commit()
115
- logging.info(f"Loaded {len(self.traffic_spots)} traffic spots")
116
 
117
  except Exception as e:
118
  logging.error(f"Error loading traffic spots: {str(e)}")
119
  self.traffic_spots = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
 
121
- def add_spots_to_map(self, folium_map):
122
- for spot in self.traffic_spots:
123
- spot.add_to_map(folium_map)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
124
 
125
  def get_spot_by_key(self, key):
126
- """Get a traffic spot by its key"""
127
- for spot in self.traffic_spots:
128
- if spot.key == key:
129
- return spot
130
- return None
 
 
 
 
 
2
  import oracledb
3
  import logging
4
  import base64
5
+ import numpy as np
6
  from html import escape
7
  from datasets import load_dataset
8
+ from datetime import datetime, timedelta
9
 
10
 
11
  class TrafficSpot:
 
14
  self.latitude = float(latitude) if latitude is not None else None
15
  self.longitude = float(longitude) if longitude is not None else None
16
  self.dataset_rows = dataset_rows or [] # List of matching dataset rows (up to 5)
17
+ self.avg_vehicle_count = self.calculate_avg_vehicle_count()
18
 
19
  def is_valid(self):
20
  return self.latitude is not None and self.longitude is not None
21
 
22
+ def calculate_avg_vehicle_count(self):
23
+ """Calculate average vehicle count from the recent data"""
24
+ if not self.dataset_rows:
25
+ return 0
26
+
27
+ # Extract vehicle counts from dataset rows
28
+ vehicle_counts = [row.get('vehicle_count', 0) for row in self.dataset_rows if 'vehicle_count' in row]
29
+
30
+ # If no valid counts are found, return 0
31
+ if not vehicle_counts:
32
+ return 0
33
+
34
+ # Calculate and return the average
35
+ return np.mean(vehicle_counts)
36
+
37
+ def get_discount_rate(self):
38
+ """Calculate discount rate based on average vehicle count"""
39
+ if self.avg_vehicle_count < 2:
40
+ return 0.20 # 20% discount
41
+ elif self.avg_vehicle_count < 5:
42
+ return 0.10 # 10% discount
43
+ else:
44
+ return 0.0 # No discount
45
+
46
+ def get_discount_info(self):
47
+ """Get discount information as a formatted string"""
48
+ discount_rate = self.get_discount_rate()
49
+
50
+ if discount_rate <= 0:
51
+ return "No traffic discount available"
52
+
53
+ return f"{int(discount_rate * 100)}% discount! Low traffic area"
54
+
55
  def create_popup_content(self):
56
+ discount_info = self.get_discount_info()
57
+ discount_display = ""
58
+
59
+ if "discount" in discount_info.lower() and "no" not in discount_info.lower():
60
+ discount_display = f"""
61
+ <div style='background-color: #e8f5e9; padding: 5px; margin: 5px 0; border-radius: 4px; border-left: 3px solid #4caf50;'>
62
+ <p style='margin: 2px 0; color: #2e7d32;'><strong>🎉 {discount_info}</strong></p>
63
+ <p style='margin: 2px 0; font-size: 0.9em;'>Avg. {self.avg_vehicle_count:.1f} vehicles per observation</p>
64
+ </div>
65
+ """
66
+
67
  html = f"""
68
  <div style='min-width: 150px; padding: 10px;'>
69
  <p style='margin: 5px 0;'><strong>Location ID:</strong> {escape(str(self.key))}</p>
70
+ {discount_display}
71
  """
72
 
73
  if self.dataset_rows:
 
106
 
107
  def add_to_map(self, folium_map):
108
  if self.is_valid():
109
+ # Choose color based on traffic level
110
+ if self.avg_vehicle_count < 2:
111
+ color = 'blue' # Low traffic - 20% discount
112
+ elif self.avg_vehicle_count < 5:
113
+ color = 'orange' # Medium traffic - 10% discount
114
+ else:
115
+ color = 'purple' # High traffic - no discount
116
+
117
  folium.Marker(
118
  location=[self.latitude, self.longitude],
119
  popup=self.create_popup_content(),
120
+ icon=folium.Icon(color=color, icon='camera'),
121
  ).add_to(folium_map)
122
 
123
 
 
125
  def __init__(self, connection_params):
126
  self.connection_params = connection_params
127
  self.traffic_spots = []
128
+ self.spot_dict = {} # For quick lookup by key
129
+ # Only load limited spots when initialized
130
+ self.load_limited_traffic_spots()
131
 
132
+ def load_limited_traffic_spots(self, limit=10):
133
+ """Load only a very limited set of traffic spots initially"""
134
  try:
135
  dataset = load_dataset("slliac/traffic-analysis", split="train")
136
  dataset_list = [row for row in dataset]
137
  dataset_list.sort(key=lambda x: x['capture_time'], reverse=True)
138
 
139
+ # Limit to just a few samples
140
  dataset_dict = {}
141
+ unique_count = 0
142
  for row in dataset_list:
143
  loc_id = row['location_id']
144
+ if unique_count >= limit:
145
+ break
146
+
147
  if loc_id not in dataset_dict:
148
  dataset_dict[loc_id] = []
149
+ unique_count += 1
150
+
151
+ if len(dataset_dict[loc_id]) < 10: # Store up to 10 records for averaging
152
  dataset_dict[loc_id].append(row)
153
 
154
  unique_locations = list(dataset_dict.keys())
 
177
  for spot in spots
178
  ]
179
 
180
+ # Build lookup dictionary
181
+ for spot in self.traffic_spots:
182
+ self.spot_dict[spot.key] = spot
183
+
184
  conn.commit()
185
+ logging.info(f"Loaded {len(self.traffic_spots)} limited traffic spots")
186
 
187
  except Exception as e:
188
  logging.error(f"Error loading traffic spots: {str(e)}")
189
  self.traffic_spots = []
190
+ self.spot_dict = {}
191
+
192
+ def load_specific_traffic_spots(self, keys):
193
+ """Load specific traffic spots by their keys"""
194
+ # Filter out keys we already have
195
+ needed_keys = [key for key in keys if key not in self.spot_dict]
196
+
197
+ if not needed_keys:
198
+ return
199
+
200
+ try:
201
+ dataset = load_dataset("slliac/traffic-analysis", split="train")
202
+ dataset_list = [row for row in dataset]
203
+ dataset_list.sort(key=lambda x: x['capture_time'], reverse=True)
204
+
205
+ dataset_dict = {}
206
+ for row in dataset_list:
207
+ loc_id = row['location_id']
208
+ if loc_id in needed_keys:
209
+ if loc_id not in dataset_dict:
210
+ dataset_dict[loc_id] = []
211
+ if len(dataset_dict[loc_id]) < 10: # Store up to 10 records for averaging
212
+ dataset_dict[loc_id].append(row)
213
+
214
+ # Only load if we have keys to load
215
+ if needed_keys:
216
+ with oracledb.connect(**self.connection_params) as conn:
217
+ cursor = conn.cursor()
218
+
219
+ # Prepare placeholders for the IN clause
220
+ placeholders = ','.join([':' + str(i + 1) for i in range(len(needed_keys))])
221
+
222
+ query = f"""
223
+ SELECT KEY, LATITUDE, LONGITUDE
224
+ FROM TD_TRAFFIC_CAMERA_LOCATION
225
+ WHERE KEY IN ({placeholders})
226
+ AND LATITUDE IS NOT NULL
227
+ AND LONGITUDE IS NOT NULL
228
+ """
229
 
230
+ cursor.execute(query, tuple(needed_keys))
231
+ spots = cursor.fetchall()
232
+
233
+ new_spots = [
234
+ TrafficSpot(
235
+ spot[0],
236
+ spot[1],
237
+ spot[2],
238
+ dataset_dict.get(spot[0])
239
+ )
240
+ for spot in spots
241
+ ]
242
+
243
+ # Add to our collections
244
+ for spot in new_spots:
245
+ self.spot_dict[spot.key] = spot
246
+ self.traffic_spots.append(spot)
247
+
248
+ conn.commit()
249
+ logging.info(f"Loaded {len(new_spots)} additional traffic spots")
250
+
251
+ except Exception as e:
252
+ logging.error(f"Error loading specific traffic spots: {str(e)}")
253
+
254
+ def add_spots_to_map(self, folium_map, spot_keys=None):
255
+ """Add only specific spots to map"""
256
+ if spot_keys is None:
257
+ # If no keys specified, add all loaded spots
258
+ for spot in self.traffic_spots:
259
+ spot.add_to_map(folium_map)
260
+ else:
261
+ # Add only the specified spots
262
+ for key in spot_keys:
263
+ if key in self.spot_dict:
264
+ self.spot_dict[key].add_to_map(folium_map)
265
 
266
  def get_spot_by_key(self, key):
267
+ """Get a traffic spot by its key, loading it if necessary"""
268
+ if key in self.spot_dict:
269
+ return self.spot_dict[key]
270
+
271
+ # Try to load it if we don't have it
272
+ self.load_specific_traffic_spots([key])
273
+
274
+ # Return if found, None otherwise
275
+ return self.spot_dict.get(key)
app.py CHANGED
@@ -63,6 +63,7 @@ def render_review_dialog():
63
  else:
64
  st.info("No reviews available for this listing.")
65
 
 
66
  def main():
67
  st.set_page_config(
68
  layout="wide",
@@ -94,6 +95,10 @@ def main():
94
  st.session_state.current_review_listing = None
95
  if 'current_review_listing_name' not in st.session_state:
96
  st.session_state.current_review_listing_name = None
 
 
 
 
97
 
98
  # Initialize visualizer with loading message for tokenizer
99
  if 'visualizer' not in st.session_state:
@@ -108,6 +113,57 @@ def main():
108
  st.error("Error initializing the application. Please refresh the page.")
109
  return
110
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  with st.sidebar:
112
  st.markdown(
113
  '<p class="sidebar-header">HKUST BNB+<BR/></p>',
@@ -133,6 +189,22 @@ def main():
133
  )
134
  show_traffic = st.checkbox("Show Traffic Cameras", value=True)
135
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
136
  if st.button("Reset All", key="reset_btn"):
137
  st.session_state.center_lat = None
138
  st.session_state.center_lng = None
@@ -140,6 +212,8 @@ def main():
140
  st.session_state.current_page = 1
141
  st.session_state.search_query = ""
142
  st.session_state.show_review_dialog = False
 
 
143
  st.rerun()
144
 
145
  # Create map and get data - pass current page information
@@ -221,7 +295,6 @@ def main():
221
  st.session_state.scroll_to_review = True
222
  st.rerun()
223
 
224
-
225
  st.markdown('</div>', unsafe_allow_html=True)
226
 
227
  # Pagination controls
 
63
  else:
64
  st.info("No reviews available for this listing.")
65
 
66
+
67
  def main():
68
  st.set_page_config(
69
  layout="wide",
 
95
  st.session_state.current_review_listing = None
96
  if 'current_review_listing_name' not in st.session_state:
97
  st.session_state.current_review_listing_name = None
98
+ if 'show_traffic_explanation' not in st.session_state:
99
+ st.session_state.show_traffic_explanation = False
100
+ if 'show_search_explanation' not in st.session_state:
101
+ st.session_state.show_search_explanation = False
102
 
103
  # Initialize visualizer with loading message for tokenizer
104
  if 'visualizer' not in st.session_state:
 
113
  st.error("Error initializing the application. Please refresh the page.")
114
  return
115
 
116
+ # Show explanations if requested
117
+ if st.session_state.show_traffic_explanation:
118
+ with st.expander("📊 Traffic-Based Discount System", expanded=True):
119
+ st.markdown("""
120
+ ### How HKUST BNB+ Acheived (E)SG , use Traffic Spot from Department of Transport and do traffic analysis hence provided discount according
121
+ to the average traffic on the previous days.
122
+
123
+ We use real-time traffic data to offer you the best possible rates:
124
+
125
+ * **Blue Camera Icons**: Areas with very low traffic (less than 2 vehicles detected)
126
+ * Enjoy a peaceful stay with **20% DISCOUNT** on these properties!
127
+
128
+ * **Orange Camera Icons**: Areas with moderate traffic (2-5 vehicles detected)
129
+ * Get a **10% DISCOUNT** on these properties!
130
+
131
+ * **Purple Camera Icons**: Areas with heavier traffic (more than 5 vehicles)
132
+ * Standard rates apply for these properties
133
+
134
+ Look for the blue connecting lines on the map to see which traffic spot affects each property!
135
+
136
+ Remark : Currently only few traffic spot avaliable, in the future will provide more.
137
+ """)
138
+ if st.button("Close", key="close_traffic_btn"):
139
+ st.session_state.show_traffic_explanation = False
140
+ st.rerun()
141
+
142
+ if st.session_state.show_search_explanation:
143
+ with st.expander("🔍 Smart Search System", expanded=True):
144
+ st.markdown("""
145
+ ### How HKUST BNB+ Acheived E(S)G , use keyword to provided semantic relevance analysis to matches the require need from HKUST Student
146
+
147
+ Our advanced search technology goes beyond simple keyword matching to understand the meaning behind your search terms:
148
+
149
+ When you search for terms like "quiet," "convenient," or "spacious," our system:
150
+ 1. Analyzes both listing titles and actual guest reviews
151
+ 2. Understands the context and meaning (not just matching exact words)
152
+ 3. Ranks listings based on overall relevance to your search
153
+
154
+ **Search Match Types:**
155
+ * **"Strong match in title and reviews"** - Perfect matches in both property description and guest experiences
156
+ * **"Strong match in listing title"** - Property description matches your needs very well
157
+ * **"Strong match in reviews"** - Guest experiences align perfectly with what you're looking for
158
+ * **"Better match in listing title/reviews"** - One source is more relevant than the other
159
+ * **"Moderate semantic match"** - Some relevance but not a perfect match
160
+
161
+ This helps you find properties that truly match what you're looking for, even if they don't use the exact words in your search!
162
+ """)
163
+ if st.button("Close", key="close_search_btn"):
164
+ st.session_state.show_search_explanation = False
165
+ st.rerun()
166
+
167
  with st.sidebar:
168
  st.markdown(
169
  '<p class="sidebar-header">HKUST BNB+<BR/></p>',
 
189
  )
190
  show_traffic = st.checkbox("Show Traffic Cameras", value=True)
191
 
192
+ st.markdown('<hr style="margin: 20px 0; border: none; border-top: 1px solid #e0e0e0;">', unsafe_allow_html=True)
193
+
194
+ # Help section in sidebar
195
+ st.markdown("### 💡 Help & Information")
196
+
197
+ col1, col2 = st.columns(2)
198
+ with col1:
199
+ if st.button("Green Discount", key="traffic_info_btn"):
200
+ st.session_state.show_traffic_explanation = True
201
+ st.rerun()
202
+
203
+ with col2:
204
+ if st.button("Semantic Search", key="search_info_btn"):
205
+ st.session_state.show_search_explanation = True
206
+ st.rerun()
207
+
208
  if st.button("Reset All", key="reset_btn"):
209
  st.session_state.center_lat = None
210
  st.session_state.center_lng = None
 
212
  st.session_state.current_page = 1
213
  st.session_state.search_query = ""
214
  st.session_state.show_review_dialog = False
215
+ st.session_state.show_traffic_explanation = False
216
+ st.session_state.show_search_explanation = False
217
  st.rerun()
218
 
219
  # Create map and get data - pass current page information
 
295
  st.session_state.scroll_to_review = True
296
  st.rerun()
297
 
 
298
  st.markdown('</div>', unsafe_allow_html=True)
299
 
300
  # Pagination controls
style.css CHANGED
@@ -417,4 +417,4 @@
417
  padding: 0 2px;
418
  border-radius: 2px;
419
  font-weight: bold;
420
- }
 
417
  padding: 0 2px;
418
  border-radius: 2px;
419
  font-weight: bold;
420
+ }