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

Added near traffic spot logic

Browse files
Files changed (4) hide show
  1. AirbnbMapVisualiser.py +100 -10
  2. TrafficSpot.py +11 -5
  3. app.py +2 -2
  4. requirements.txt +2 -1
AirbnbMapVisualiser.py CHANGED
@@ -3,8 +3,10 @@ import pandas as pd
3
  import folium
4
  from html import escape
5
  from sentence_transformers import SentenceTransformer, util
6
- from TrafficSpot import TrafficSpotManager
 
7
 
 
8
 
9
  class AirbnbMapVisualiser:
10
  def __init__(self):
@@ -22,7 +24,10 @@ class AirbnbMapVisualiser:
22
  increment=1,
23
  getmode=oracledb.SPOOL_ATTRVAL_WAIT
24
  )
 
 
25
  self.traffic_manager = TrafficSpotManager(self.connection_params)
 
26
 
27
  # Initialize sentence transformer model
28
  try:
@@ -45,6 +50,32 @@ class AirbnbMapVisualiser:
45
  self.cached_listings = {}
46
  self.cached_embeddings = {}
47
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  def get_all_neighborhoods(self):
49
  connection = self.pool.acquire()
50
  try:
@@ -267,7 +298,6 @@ class AirbnbMapVisualiser:
267
  return [0.0] * len(df)
268
 
269
  def sort_by_relevance(self, df, search_query):
270
- """Sort listings by relevance using sentence transformer comparison"""
271
  if not search_query:
272
  return df
273
 
@@ -370,13 +400,54 @@ class AirbnbMapVisualiser:
370
  tiles='OpenStreetMap'
371
  )
372
 
 
 
 
 
 
 
 
 
 
373
  for idx, row in df.iterrows():
374
  marker_id = f"marker_{row['id']}"
375
  reviews = self.get_listing_reviews(row['id'])
376
  review_button_key = f"review_btn_{row['id']}"
377
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  relevance_info = ""
379
- if search_query and 'relevance_percentage' in row:
380
  relevance_info = f"""
381
  <div class='relevance-info' style='margin: 10px 0; padding: 8px; background-color: #f8f9fa; border-radius: 4px;'>
382
  <p style='margin: 5px 0;'>
@@ -396,12 +467,8 @@ class AirbnbMapVisualiser:
396
  <p style='margin: 5px 0;'><strong>Room Type:</strong> {escape(str(row['room_type']))}</p>
397
  <p style='margin: 5px 0;'><strong>Price:</strong> ${row['price']:.0f}</p>
398
  <p style='margin: 5px 0;'><strong>Reviews:</strong> {row['number_of_reviews']:.0f}</p>
 
399
  {relevance_info}
400
- <button onclick="streamlit_click('{review_button_key}')"
401
- style="background-color: #4CAF50; color: white; padding: 8px 15px; border: none;
402
- border-radius: 4px; cursor: pointer; margin-top: 10px; width: 100%;">
403
- View Reviews ({len(reviews)})
404
- </button>
405
  </div>
406
  """
407
 
@@ -416,7 +483,30 @@ class AirbnbMapVisualiser:
416
  if selected_id is not None and row['id'] == selected_id:
417
  marker._name = marker_id
418
 
419
- if show_traffic:
420
- self.traffic_manager.add_spots_to_map(m)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
421
 
422
  return m, df
 
3
  import folium
4
  from html import escape
5
  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):
 
24
  increment=1,
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
 
32
  # Initialize sentence transformer model
33
  try:
 
50
  self.cached_listings = {}
51
  self.cached_embeddings = {}
52
 
53
+ def find_nearest_traffic_spot(self, airbnb_lat, airbnb_lng, max_distance_km=1.0):
54
+ """Find the nearest traffic spot within max_distance_km kilometers of an Airbnb listing"""
55
+ nearest_spot = None
56
+ min_distance = float('inf')
57
+
58
+ # Check each traffic spot
59
+ for spot in self.traffic_manager.traffic_spots:
60
+ if not spot.is_valid():
61
+ continue
62
+
63
+ # Calculate distance in kilometers
64
+ distance = geodesic(
65
+ (airbnb_lat, airbnb_lng),
66
+ (spot.latitude, spot.longitude)
67
+ ).kilometers
68
+
69
+ # Update nearest if this spot is closer and within max distance
70
+ if distance < min_distance and distance <= max_distance_km:
71
+ min_distance = distance
72
+ nearest_spot = spot
73
+
74
+ if nearest_spot:
75
+ return nearest_spot, min_distance
76
+ else:
77
+ return None, None
78
+
79
  def get_all_neighborhoods(self):
80
  connection = self.pool.acquire()
81
  try:
 
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
 
 
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
+
426
+ traffic_spot_info = f"""
427
+ <div class='traffic-spot-info' style='margin: 10px 0; padding: 8px; background-color: #f0f8ff; border-radius: 4px; border-left: 4px solid #4285f4;'>
428
+ <p style='margin: 5px 0;'>
429
+ <strong>Nearest Traffic Spot:</strong> {escape(str(nearest_spot.key))}
430
+ <br/>
431
+ <strong>Distance:</strong> {distance_str}
432
+ </p>
433
+ </div>
434
+ """
435
+
436
+ # Add a line connecting Airbnb to nearest traffic spot
437
+ folium.PolyLine(
438
+ locations=[
439
+ [row['latitude'], row['longitude']],
440
+ [nearest_spot.latitude, nearest_spot.longitude]
441
+ ],
442
+ color='blue',
443
+ weight=2,
444
+ opacity=0.7,
445
+ dash_array='5',
446
+ tooltip=f"Distance: {distance_str}"
447
+ ).add_to(lines_group)
448
+
449
  relevance_info = ""
450
+ if search_query and 'relevance_percentage' in row and 'relevance_features' in row:
451
  relevance_info = f"""
452
  <div class='relevance-info' style='margin: 10px 0; padding: 8px; background-color: #f8f9fa; border-radius: 4px;'>
453
  <p style='margin: 5px 0;'>
 
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>
473
  """
474
 
 
483
  if selected_id is not None and row['id'] == selected_id:
484
  marker._name = marker_id
485
 
486
+ # Add JavaScript function to help navigate to traffic spots
487
+ folium.Element("""
488
+ <script>
489
+ function showTrafficSpot(lat, lng) {
490
+ // Get the map object
491
+ var map = document.querySelector('.folium-map')._leaflet_map;
492
+
493
+ // Pan to the traffic spot and zoom in
494
+ map.setView([lat, lng], 18);
495
+
496
+ // Find and open the popup for the traffic spot marker
497
+ map.eachLayer(function(layer) {
498
+ if (layer instanceof L.Marker) {
499
+ var latLng = layer.getLatLng();
500
+ if (Math.abs(latLng.lat - lat) < 0.0001 && Math.abs(latLng.lng - lng) < 0.0001) {
501
+ layer.openPopup();
502
+ }
503
+ }
504
+ });
505
+ }
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
TrafficSpot.py CHANGED
@@ -1,10 +1,9 @@
1
- import logging
2
- from datetime import datetime
3
- from html import escape
4
  import folium
5
  import oracledb
6
- from datasets import load_dataset
7
  import base64
 
 
8
 
9
 
10
  class TrafficSpot:
@@ -121,4 +120,11 @@ class TrafficSpotManager:
121
 
122
  def add_spots_to_map(self, folium_map):
123
  for spot in self.traffic_spots:
124
- spot.add_to_map(folium_map)
 
 
 
 
 
 
 
 
 
 
 
1
  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:
 
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
app.py CHANGED
@@ -129,7 +129,7 @@ def main():
129
  neighborhood = st.selectbox(
130
  "Select Neighborhood",
131
  options=visualizer.neighborhoods,
132
- index=visualizer.neighborhoods.index("Southern") if "Southern" in visualizer.neighborhoods else 0
133
  )
134
  show_traffic = st.checkbox("Show Traffic Cameras", value=True)
135
 
@@ -142,7 +142,7 @@ def main():
142
  st.session_state.show_review_dialog = False
143
  st.rerun()
144
 
145
- # Create map and get data
146
  m, df = visualizer.create_map_and_data(
147
  neighborhood,
148
  show_traffic,
 
129
  neighborhood = st.selectbox(
130
  "Select Neighborhood",
131
  options=visualizer.neighborhoods,
132
+ index=visualizer.neighborhoods.index("Kowloon City") if "Kowloon City" in visualizer.neighborhoods else 0
133
  )
134
  show_traffic = st.checkbox("Show Traffic Cameras", value=True)
135
 
 
142
  st.session_state.show_review_dialog = False
143
  st.rerun()
144
 
145
+ # Create map and get data - pass current page information
146
  m, df = visualizer.create_map_and_data(
147
  neighborhood,
148
  show_traffic,
requirements.txt CHANGED
@@ -15,4 +15,5 @@ pillow~=10.4.0
15
  timm~=1.0.14
16
  streamlit_folium~=0.24.0
17
  watchdog
18
- sentence_transformers
 
 
15
  timm~=1.0.14
16
  streamlit_folium~=0.24.0
17
  watchdog
18
+ sentence_transformers
19
+ geopy