Spaces:
Sleeping
Sleeping
Gordon Li
commited on
Commit
·
4a995f4
1
Parent(s):
c6645ce
Added near traffic spot logic
Browse files- AirbnbMapVisualiser.py +100 -10
- TrafficSpot.py +11 -5
- app.py +2 -2
- 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
|
|
|
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 |
-
|
420 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
-
|
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("
|
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
|